ステージング環境は「開発途中の機能を関係者だけで確認したい」「検索エンジンに拾われたくない」「外部にURLが漏れたときの被害を抑えたい」といった理由で、アクセス制限を入れるケースが多いです。
その中でも Basic認証は、
- ブラウザだけで完結(追加UI不要)
- 実装コストが低い
- とりあえずの“門番”として十分機能する
という理由で、ステージング用途では定番です。
この記事では、Laravel側で APP_ENV=staging のときだけ Basic認証を要求し、それ以外(local / production 等)では素通しにする実装を紹介します。
方式の選択肢:Laravelでかける?Webサーバでかける?
ステージングにBasic認証をかける方法は大きく2つです。
A. Laravel(アプリ)側でかける(本記事のメイン)
メリット
- 環境判定(stagingのみ)をコードで完結できる
- ルート単位で適用範囲を変えやすい(管理画面だけ、特定ツールだけ、など)
- インフラ変更なしで導入可能(PaaSでもやりやすい)
デメリット
- Laravelまでリクエストが到達する(=アプリの前段では止まらない)
- 静的ファイル配信(画像等)には効かない構成もある(配信経路次第)
B. Webサーバ(Nginx/Apache)側でかける
メリット
- Laravelに到達する前に遮断できる(負荷・攻撃耐性の観点で強い)
- 静的ファイルも含めて一律で守りやすい
デメリット
- 環境ごとにサーバ設定が必要(ホスティング形態によっては触れない)
- “ルート単位の柔軟さ”は落ちる(設定で頑張る必要がある)
結論として、「stagingだけ」「早く入れたい」「運用もしやすくしたい」なら Laravelミドルウェア方式が第一候補になります。
実装手順(Laravelミドルウェア方式)
1) Middleware を作成
app/Http/Middleware/StagingBasicAuth.php を作成します(php artisan make:middleware StagingBasicAuth でもOK)。
ポイントは以下です。
- staging 以外は 即
next()で素通し - ID/PW は 環境変数(
.env) で管理 hash_equalsで比較(タイミング攻撃対策)- stagingなのにID/PW未設定なら 500で落として“設定漏れに気付ける” ようにする(運用向け)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class StagingBasicAuth
{
public function handle(Request $request, Closure $next): Response
{
// staging 以外は無効(素通し)
if (!app()->environment('staging')) {
return $next($request);
}
$user = (string) env('BASIC_AUTH_USER', '');
$pass = (string) env('BASIC_AUTH_PASS', '');
// staging なのに設定が空なら事故るので 500 にして気付けるようにする(運用向け)
if ($user === '' || $pass === '') {
abort(500, 'BASIC_AUTH_USER / BASIC_AUTH_PASS is not set for staging.');
}
$givenUser = (string) $request->getUser();
$givenPass = (string) $request->getPassword();
// timing attack 対策:hash_equals
if (!hash_equals($user, $givenUser) || !hash_equals($pass, $givenPass)) {
return response('Unauthorized', 401, [
'WWW-Authenticate' => 'Basic realm="Staging"',
]);
}
return $next($request);
}
}
2) bootstrap/app.php に middleware を登録
Laravel 11以降は、ミドルウェア周りの設定が bootstrap/app.php に集約されています(withMiddleware)。
グローバルに適用したい場合は append() で追加します。
use App\Http\Middleware\StagingBasicAuth;
->withMiddleware(function (Middleware $middleware): void {
// ーバルミドルウェア(全HTTPリクエストに適用)
$middleware->append(StagingBasicAuth::class);
}
3) .env に認証情報を追加(stagingのみ)
APP_ENV=staging
BASIC_AUTH_USER=your_user
BASIC_AUTH_PASS=your_password
運用の注意
- パスワードは推測されにくい長さにする(最低でもランダム16文字程度)
- 共有範囲が広いなら、定期ローテーション前提にする
- 認証情報をGit管理しない(
.envをコミットしない)
「全ページ」ではなく「特定ルートだけ」にかけたい場合
テージングでも、例えば以下は“素通しにしたい”ことがあります。
- ヘルスチェックURL(ロードバランサ等)
- Webhook受信エンドポイント
- 特定の検証URLだけ関係者に公開したい
その場合は グローバル適用ではなく、ルート/グループにだけ適用にすると運用が楽です。
例:ルートミドルウェアとして alias 登録して使う
bootstrap/app.php でエイリアス登録(例)
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'staging.basic' => \App\Http\Middleware\StagingBasicAuth::class,
]);
})
ルート側
Route::middleware(['staging.basic'])->group(function () {
Route::get('/admin', fn () => 'admin');
Route::get('/tools', fn () => 'tools');
});
こうすると「必要な範囲だけ守る」構成にできます。