Laravelのカスタムミドルウェア作成と活用法:認証以外の横断処理を安全に差し込む
- 作成日 2026.02.17
- その他
ミドルウェアは、コントローラに入る前/レスポンスを返す前後に「横断的な処理」を挟む仕組み。認証・CSRFだけでなく、IP制限、メンテナンス制御、リクエストID付与、APIキー検証、利用規約同意チェック、ロール制御、ヘッダ付与、レート制限などに使える。この記事は、カスタムミドルウェアの作り方から、ルート/グループへの適用、引数付きミドルウェア、デバッグ、よくあるエラー条件までを実務向けに整理する。
ミドルウェアが効く場面:コントローラに書くと増殖する処理を1箇所に寄せる
同じチェックが複数コントローラに散ると、仕様変更で漏れが出る。ミドルウェアにすると「入口で必ず通る」形になり、適用範囲(全体/特定ルート/特定グループ)で制御できる。
典型例:APIキー必須、特定IPだけ許可、管理画面のみ追加ヘッダ、リクエストログ、利用規約同意の強制など。
作成手順:make:middleware で雛形を作る
まずは雛形を作り、handle() で通す/止めるを決める。
php artisan make:middleware EnsureApiKey
// app/Http/Middleware/EnsureApiKey.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureApiKey
{
public function handle(Request $request, Closure $next): Response
{
// ここでチェックして通す/止める
return $next($request);
}
}サンプル:APIキー必須ミドルウェア(不足時は401)
ヘッダ X-API-KEY を必須にし、値が一致しない場合は拒否する例。エラー条件が明確なので、実務で使い回しやすい。
// app/Http/Middleware/EnsureApiKey.php(例)
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureApiKey
{
public function handle(Request $request, Closure $next): Response
{
$provided = $request->header('X-API-KEY'); // エラー条件①:ヘッダ未設定でnull
$expected = config('app.internal_api_key'); // エラー条件②:設定未投入でnull
if (!$expected) {
// 設定ミスは運用事故なので 500 に寄せて気づけるようにする
return response()->json([
'message' => 'Server misconfigured: API key not set.'
], 500);
}
if (!$provided || !hash_equals($expected, $provided)) { // エラー条件③:不一致
return response()->json([
'message' => 'Unauthorized.'
], 401);
}
return $next($request);
}
}登録と適用:ルート/グループに付けて範囲を固定する
ミドルウェアは「どこに適用するか」が一番重要。全体に入れると影響範囲が広すぎることがあるので、まずはルートグループ単位が安全。
// routes/api.php(例)
use Illuminate\Support\Facades\Route;
Route::middleware(['ensure.api.key'])->group(function () {
Route::get('/internal/health', fn () => ['ok' => true]);
Route::post('/internal/sync', fn () => ['queued' => true]);
});※エイリアス(ensure.api.key)の登録方法はプロジェクトのLaravelバージョン構成に依存するため、ミドルウェアの登録箇所(Kernel/Bootstrap/Providerなど)に合わせて1箇所で統一するのが前提。
引数付きミドルウェア:同じ処理を条件だけ変えて使い回す
IP制限やロール制限などは、引数で動きを変えると便利。例として「特定ロールだけ許可」の形。
// handle の第3引数以降に引数を受けられる例
public function handle(Request $request, Closure $next, string $role): Response
{
$user = $request->user(); // エラー条件①:未ログインだとnull
if (!$user) {
return redirect()->route('login'); // もしくは 401
}
if ($user->role !== $role) { // エラー条件②:権限不足
abort(403);
}
return $next($request);
}前処理/後処理:レスポンスにヘッダを足す、実行時間を測る
ミドルウェアは「レスポンスを返す前後」で処理できる。入口で時間計測→出口でログ化、レスポンスヘッダ追加などが定番。
public function handle(Request $request, Closure $next): Response
{
$start = microtime(true);
$response = $next($request);
$elapsedMs = (microtime(true) - $start) * 1000;
$response->headers->set('X-Request-Id', (string) $request->header('X-Request-Id', uniqid()));
$response->headers->set('X-Elapsed-Ms', (string) (int) $elapsedMs);
logger()->info('request.done', [
'path' => $request->path(),
'elapsed_ms' => (int) $elapsedMs,
]);
return $response;
}よくあるエラー条件:動かない/効かない/無限リダイレクト
ミドルウェア周りで詰まりやすいポイントと発生条件。
・ルートに付けたのに効かない:登録(エイリアス/クラス指定)ができていない、適用グループが違う
・401/403が連発:認証ミドルウェアより先に権限チェックしている(順序問題)
・無限リダイレクト:未ログイン→loginへ→loginにも同じミドルウェアが掛かっている
・CORS/プリフライトが落ちる:OPTIONSを弾いている(APIキー強制など)
・500になる:config値が未設定、env名のtypo、キャッシュが古い(config:cache)
ミドルウェアの“順番”と“適用範囲”が原因の大半を占める。
デバッグ手順:ミドルウェア内ログ→ルート単位で切り分ける
動作確認は「当たっているか」と「条件分岐がどこで落ちたか」を分ける。
・handle() の最初にログを1行入れる(本番は抑制)
・ヘッダやユーザーID、ルート名をログ化
・まず単一ルートにだけ適用→問題なければグループへ拡張
ミドルウェアの不具合は影響範囲が広いので、適用範囲は段階的に広げるのが安全。
まとめ:カスタムミドルウェアを安全に運用する要点
・横断処理(認証以外の共通チェック)を入口に集約できる
・まずはルートグループ単位で適用し、影響範囲を管理する
・重い処理は入れすぎない(ログ/外部APIは慎重に)
・引数付きで再利用し、分岐条件を明確にする
・エラー条件(未ログイン、ヘッダ不足、権限不足、設定ミス)をレスポンスとログで区別する
・順序問題(authより先に権限チェック等)と無限リダイレクトを最優先で潰す
-
前の記事
Laravelのソフトデリートとデータリストア:論理削除を安全に運用する実装パターン 2026.02.16
-
次の記事
Laravel×Vue.jsでSPAを構築する:API分離・認証・ルーティング・デプロイまでの実務パターン 2026.02.18
コメントを書く