Laravel

Laravelのミドルウェアとは|適用範囲の使い分けと実行順序の決め方

Laravelのミドルウェアとは|適用範囲の使い分けと実行順序の決め方

Laravelのミドルウェアは、コントローラに入る前のリクエストを検査して通すか止めるかを決める仕組みです。認証チェックもCSRF対策も同じ土俵の上に乗っているため、これを押さえるとリクエストがどの順に何を通っているかが読めるようになります。つまずきやすいのは、日本語の情報がapp/Http/Kernel.phpに配列を書き足す時代のまま止まっている点でしょう。この記事は2026年8月30日時点の公式ドキュメントと、laravel/frameworkの13.xブランチのソースを実測した内容で構成しています。なお、OSとアプリケーションの間に置くソフトウェア分類としての用語はミドルウェアとは?OSとアプリの間で働く3種類と選定・運用の判断を実装者向けに解説で扱っており、本記事とは指しているものが違います。

まとめ:登録先の選び方と実行順序の判断

  • 登録先は「全リクエスト向けのグローバル」「経路のまとまり向けのグループ」「個別ルート向け」の3層です。書く場所はいずれもbootstrap/app.phpに集約されています。
  • 11以降のスケルトンにKernel.phpはありません。配列を直接編集する解説を見つけたら、その記事は10以前を前提にしていると考えて読み替えます。
  • 通過順は往路がグローバル、グループ、ルートの順で、復路はその逆をたどります。レスポンスを加工する処理はこの反転を前提に置き場所を決めます。
  • 順序を固定したいときはpriorityで並びを宣言するか、13系で追記されたprependToPriorityListで既存の並びへ差し込みます。
  • 13系ではCSRF検証の担当がPreventRequestForgeryへ移りました。validateCsrfTokensは非推奨で、内部的に読み替えられる状態です。
  • グローバルへ置いてよいのは、全経路で例外が発生しない処理だけだと判断しています。1つでも除外条件が出た時点でグループかルートへ降ろします。

ミドルウェアはリクエストが本体に届く前後へ挟まるアプリケーション処理の層

動きは単純です。届いたHTTPリクエストは、コントローラへ渡る前にいくつかのクラスを順に通ります。各クラスは中身を覗いて、条件を満たさなければリダイレクトや例外で打ち切り、問題なければ次へ回します。Laravelが同梱している認証チェックやCSRF対策も、利用者が書く自作クラスも、まったく同じ仕組みの上に並んでいる点が要点です。

インフラ用語のミドルウェアとはアプリ開発で指す対象と役割の範囲が異なる

同じ言葉ですが、指す対象は別です。インフラの文脈での用語は、OSとアプリケーションの間に置くソフトウェア分類、つまりWebサーバやAPサーバ、DBMS、メッセージング基盤などを指します。一方Laravelの用語は、フレームワーク内部のリクエスト処理経路に挟まるPHPクラスのことです。設計会議で両者が混線すると話が噛み合わなくなるため、社内では「HTTPミドルウェア」と呼び分けておくと安全でしょう。

handleメソッドと引数の$nextが前処理と後処理を分ける層の境目になる

クラスの中身はhandleメソッド1本です。第1引数にリクエスト、第2引数に$nextというクロージャを受け取り、$next($request)を呼ぶと処理が次の層へ進みます。呼ばずにredirectやabortを返せば、そこでリクエストは止まります。公式ドキュメントが示す構造は次のとおりです。

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureTokenIsValid
{
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->input('token') !== 'my-secret-token') {
            return redirect('/home');
        }

        return $next($request);
    }
}

依存はコンストラクタで型宣言すればサービスコンテナが解決します。ログ出力用のクラスや設定リポジトリを受け取りたいときは、handleの中で静的に呼ぶよりコンストラクタ注入のほうが後で差し替えやすくなるでしょう。

前処理と後処理は$nextを呼ぶ位置とレスポンスの扱いで書き分ける

同じhandleでも、$next($request)を呼ぶ位置で役割が変わります。呼ぶ前に処理を書けばリクエストが本体へ届く前に走り、戻り値を受け取ってから処理を書けばレスポンスが返る直前に走ります。ヘッダを足す、レスポンス時間を計測するといった用途は後者です。1つのクラスに前後の両方を詰め込むこともできますが、責務が読み取りづらくなるので分けたほうが保守は楽になります。

登録先は適用範囲に応じてグローバル・グループ・ルートの3層に分かれる

クラスを書いただけでは何も起きません。どの範囲に効かせるかを登録して初めて経路へ組み込まれます。11以降はapp/Http/Kernel.phpが廃止され、登録はすべてbootstrap/app.phpのwithMiddlewareクロージャに集約されました。実際にスケルトンの13.xブランチを確認したところ、このクロージャは初期状態で空で、app/Http配下にあるのはControllersだけです。Middlewareディレクトリは生成コマンドを叩くまで作られません。

グローバル登録はappendとprependでスタック内の前後を決める

全リクエストに効かせるならappendを使います。既定スタックの末尾へ足す動きで、先頭へ入れたい場合はprependです。Laravelが持っている既定のグローバルスタックはInvokeDeferredCallbacks、TrustProxies、HandleCors、PreventRequestsDuringMaintenance、ValidatePostSize、TrimStrings、ConvertEmptyStringsToNullで、TrustHostsはコメントアウトされた状態で示されています。この並び自体を丸ごと差し替えたいときはuseメソッドに配列を渡します。

use App\Http\Middleware\EnsureTokenIsValid;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->append(EnsureTokenIsValid::class);
})

webとapiのグループには既定の顔ぶれが決まっているため内容を確認する

グループは複数のクラスを1つの名前で束ねたものです。routes/web.phpにはwebグループ、routes/api.phpにはapiグループが自動で当たります。13系での既定の中身は次のとおりです。

グループ 既定で入るクラス 役割
web EncryptCookies Cookieの暗号化と復号
web 予約Cookieをレスポンスへ付与 予約Cookieの付与
web StartSession セッションの開始
web ShareErrorsFromSession 検証エラーのビュー共有
web PreventRequestForgery リクエスト偽造の防止
web SubstituteBindings ルートのモデル解決
api SubstituteBindings ルートのモデル解決

SubstituteBindingsはルートパラメータをモデルへ差し替える担当で、コントローラの引数にモデルを型宣言できるのはこのクラスのおかげです。モデル解決そのものの挙動はLaravel Eloquentとは|モデル定義・リレーション・eager loadingの基礎を併せて読むと結び付きます。StartSessionが担うセッション周りの設定はLaravelのセッション管理|ドライバ選定とCSRF・複数台構成の共有設計に切り出しました。

グループへの追加はwebとapiのメソッドが近道で、名前付き引数のappendやprependで位置を指定します。独自の名前でグループを作るならappendToGroupです。設定クラスにはremoveFromGroupやreplaceInGroupも用意されており、既定の1本だけを自作クラスへ置き換えるといった細工もできます。

->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [EnsureUserIsSubscribed::class]);
    $middleware->api(prepend: [EnsureTokenIsValid::class]);
    $middleware->appendToGroup('admin', [EnsureUserHasRole::class]);
})

ルート単位の割り当てとエイリアスによる短縮を適用範囲ごとに使い分ける

個別のルートへ当てるならルート定義にmiddlewareを鎖状につなぎます。複数当てるときは配列です。長いクラス名を毎回書くのが煩わしければaliasで短い名前を定義できます。既定のエイリアスはauth、auth.basic、auth.session、cache.headers、can、guest、password.confirm、precognitive、signed、throttle、verifiedの11個です。subscribedも一覧に載っていますが、ソースを読むとSparkのクラスが存在するときだけ追加される条件付きなので、素のアプリでは使えません。

Route::middleware(['auth', 'verified'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::get('/status', [StatusController::class, 'show'])
        ->withoutMiddleware(['verified']);
});

グループの中で1本だけ外したいときはwithoutMiddlewareです。ただしこれで外せるのはルートに割り当てられたものだけで、グローバル登録には効きません。ヘルスチェック用のエンドポイントをメンテナンス中も応答させたい、といった要件はグローバル側の除外設定で扱います。

コントローラ側で指定する書き方と除外の指定でメソッド単位の適用範囲を絞る

ルートファイルではなくコントローラに寄せる書き方もあります。HasMiddlewareインターフェースを実装して静的なmiddlewareメソッドから配列を返す形が従来からの方法で、onlyとexceptでアクションを絞れます。13系ではこれに加えてPHP属性が使えるようになりました。クラスに付けた指定とメソッドに付けた指定はマージされます。

use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class UserController
{
    #[Middleware('subscribed', only: ['store'])]
    public function store()
    {
    }
}

リソースルートを使っている場合は、middlewareForでアクション単位に付け、withoutMiddlewareForで外せます。indexだけ認証を外して一覧を公開する、といった要件をRoute::resourceを崩さずに書けるのが利点です。

自作ミドルウェアはmake:middlewareで生成して登録する

生成はphp artisan make:middleware EnsureUserHasRoleです。クラス名は処理内容を述語で表す形が公式の例に沿った付け方で、EnsureやVerifyで始めると意図が読み取りやすくなります。プロジェクト全体の命名を揃えたい場合はLaravelの命名規則一覧|テーブル・モデル・規約と自動チェックまでの方針に合わせておくと、レビューでの指摘が減るでしょう。

パラメータ付きの呼び出しでクラスを使い回してルートごとの条件を切り替える

役割ごとにクラスを量産する必要はありません。handleの第3引数以降を追加すると、ルート側ではコロン区切りで値を渡せる仕組みです。複数の値はカンマで並べます。ロールや権限のように「同じ判定ロジックで対象だけ違う」ケースはこれで1クラスに収まります。

public function handle(Request $request, Closure $next, string $role): Response
{
    if (! $request->user()->hasRole($role)) {
        abort(403);
    }

    return $next($request);
}
Route::put('/post/{id}', [PostController::class, 'update'])
    ->middleware(EnsureUserHasRole::class.':editor,publisher');

レスポンス送信後に動くterminableミドルウェアを使う条件

クラスにterminateメソッドを定義しておくと、レスポンスをブラウザへ送り終えたあとに呼ばれます。動作条件としてFastCGIでの実行が前提です。アクセス記録の書き出しのように、利用者を待たせたくない後処理に向いています。注意したいのは、terminateの呼び出し時にコンテナから新しいインスタンスが解決される点で、handleで貯めたプロパティは引き継がれません。同じインスタンスを使いたい場合はAppServiceProviderのregisterでsingletonとして登録します。

もっとも、送信後の処理が数百ミリ秒を超えるならワーカー側へ逃がすほうが素直です。判断の分かれ目は「失敗したときに再試行したいか」で、再試行が要るならLaravel Queueの使い方|ジョブ定義からworker常駐と失敗ジョブ運用までで扱ったキューに寄せます。

実行順序は登録位置で決まり、必要な場合はpriorityで上書きできる

複数を組み合わせると、どれが先に走るかが問題になります。基本の並びはグローバル、グループ、ルートの順です。ここで見落とされがちなのが、この順序が往路だけの話だという点でしょう。

往路と復路で通過順が反転する入れ子構造とパイプラインの仕組み

パイプラインは入れ子です。リクエストはグローバルの外側から入ってルートの内側へ進み、コントローラの戻り値は逆順で外へ向かう流れです。したがって$nextの前に書いた処理はグローバルが最初に走り、$nextの後ろに書いた処理はルートに割り当てたものが最初に走ります。レスポンスヘッダを上書きする処理をグローバルに置いたのに、ルート側の値で潰されるという事故はこの反転を見落としたときに起きます。

priorityとappendToPriorityListで並びを固定する

登録位置だけでは順序を制御しきれない場面もあります。パッケージが勝手に足したクラスと自作クラスの前後関係を決めたいときなどです。この場合はpriorityに配列を渡して全体の並びを宣言します。ただし全体を書き直す形になるので、フレームワーク側の既定が更新されたときに追随が要ります。

13系のドキュメントには、既存の並びを保ったまま差し込む方法が追記されました。prependToPriorityListは指定クラスの直前へ、appendToPriorityListは直後へ入る仕組みです。12系の同じ節にこの記載はなく、設定クラスの実装にもこの2メソッドが増えています。基準にするクラスは配列でも渡せます。

->withMiddleware(function (Middleware $middleware): void {
    $middleware->prependToPriorityList(
        before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
        prepend: \App\Http\Middleware\EnsureTokenIsValid::class,
    );
})

順序への依存そのものを減らす設計へ寄せて変更時の保守負担を抑える

とはいえ、順序指定に頼る構成は将来の版上げで壊れやすくなります。実務では、順序に依存しない形へ寄せられないかを先に検討したほうが持ちます。判定に必要な情報を前のクラスがリクエスト属性へ詰める作りをやめ、各クラスが自分で必要な情報を取りに行く形にすれば、前後関係は問題になりません。順序の固定が要るのは、認証済みであることを前提に権限を見るような、依存が本質的な組み合わせに限られます。

Laravel 13系でCSRF検証はPreventRequestForgeryへ移った

版差で最も引っかかりやすいのがここです。12系までwebグループの5番目に入っていたのはValidateCsrfTokenでしたが、13系ではPreventRequestForgeryに置き換わりました。公式のリリースノートでは、トークン方式との互換を保ったままオリジン検証を加えた強化だと説明されています。

validateCsrfTokensは非推奨で内部的に読み替えられる

設定側のメソッドも変わりました。13.xの設定クラスを読むと、validateCsrfTokensには非推奨の注記が付き、実装はpreventRequestForgeryを呼ぶだけになっています。新しいメソッドは第2引数以降にoriginOnlyとallowSameSiteを取り、オリジンだけで判定するか、同一サイト扱いを許すかを切り替えられます。既存コードは動きますが、新規に書くなら新しい名前を使うのが筋でしょう。

->withMiddleware(function (Middleware $middleware): void {
    $middleware->preventRequestForgery(
        except: ['webhooks/*'],
        originOnly: false,
        allowSameSite: false,
    );
})

外部サービスからのWebhookを受ける経路は、この除外設定に入れないとトークンを持たないPOSTが弾かれます。419が返ってくる場面の切り分け手順はInvalid CSRF tokenの意味と直し方|Laravel 419・Django対応に整理しました。SPAやモバイルからのアクセスをトークン認証で受ける構成なら、statefulApiの有無と併せてLaravel Sanctumとは:SPAとモバイルのトークン認証とCookieセッションの使い分けの判断軸で決めます。

版を上げる前に各バージョンのサポート期限と確認先を事前に確かめる

公式のサポート表を実測すると、12系のバグ修正は2026年8月13日で終わり、セキュリティ修正だけが2027年2月24日まで続く状態です。11系はセキュリティ修正も2026年3月12日で終了しています。13系は2026年3月17日リリースで、対応PHPは8.3から8.5、本体の最新は執筆時点でv13.29.0(2026年8月25日公開)でした。手元のプロジェクトがどの版かはLaravelのバージョン確認|artisanとcomposerの違いとサポート期限の手順で確かめられます。

登録先を決める条件と、ミドルウェアとして書かずに済ませる判断基準

ここからは受託の現場でどう決めているかを言い切ります。3層のどこへ置くかは好みではなく、除外条件の有無で機械的に決まります。

グローバルに置いてよいのは全経路で例外のない共通処理だけと判断する

全経路で無条件に走ってよいと言い切れる処理だけをグローバルへ置きます。該当するのは、IPアドレスの正規化、リクエストIDの採番、共通のセキュリティヘッダ付与などです。逆に、1つでも「この画面だけは除きたい」が出たらグループへ降ろします。グローバルに置いたうえで内部の条件分岐で逃がす作りは、除外条件が増えるたびにクラスが肥大し、どのURLが対象なのかコードを読まないと分からなくなるため採りません。

権限判定のように業務ルールを含むものはルート単位です。管理画面と一般画面のように経路がはっきり分かれるならグループを作り、appendToGroupで束ねます。APIの経路設計そのものはLaravel APIの作り方|install:apiとAPIリソース・エラー応答の統一の方針に沿わせると、グループ分けとルーティングの粒度が揃います。

見送る場面は分岐が業務条件に依存するときのコントローラへ移す判断基準

ミドルウェアで書くべきでない処理もあります。判定に業務データの読み書きが必要なもの、判定結果によって返す画面が案件ごとに変わるものは、コントローラかフォームリクエストへ寄せます。理由は単純で、ミドルウェアは経路に紐づく仕組みなので、業務条件を持ち込むとテストのたびにHTTP経路ごと組み立てる必要が生じるからです。契約更新の有無で表示を切り替えるといった要件は、ミドルウェアで403を返すのではなくコントローラ内で分岐させたほうが、単体テストの粒度を保てます。

もう1つの見送り基準が処理時間です。全リクエストで外部APIを叩くような処理をグローバルへ置くと、その応答時間が全画面の応答時間へそのまま加算される形です。判定結果をキャッシュできないなら、ミドルウェアではなく非同期側へ回す設計にします。こうした横断処理の切り分けを含めて設計から任せたい場合は、業務用・Webアプリ開発で認証・権限・監査ログの実装まで引き受けています。

よくある質問

Kernel.phpが見つからないのはなぜですか?

11以降のスケルトンでは廃止されたためです。実際に13.xブランチを確認すると、app/Http配下にあるのはControllersディレクトリだけでした。登録先はbootstrap/app.phpのwithMiddlewareクロージャに移っています。10以前から上げたプロジェクトではKernel.phpが残っている場合もあり、その状態なら従来どおり配列を編集します。

ミドルウェアグループと個別指定はどちらを使うべきですか?

対象がURLのまとまりならグループ、単発のルートなら直接指定という切り分けで足ります。3本以上のルートに同じ組み合わせを当てているなら、名前を付けてグループへ寄せたほうが後の変更が1か所で済みます。逆に1本しか使わない指定にグループ名を与えると、名前から中身を推測できなくなるので避けましょう。

ミドルウェアの中でリクエストの値を書き換えてよいですか?

技術的には可能ですが、乱用は避けたい書き方です。入力値の整形はTrimStringsのように全経路へ一律で効かせる性質のものに限り、業務的な補完はフォームリクエスト側で行うほうが追跡しやすくなります。書き換えた事実がコントローラから見えないため、値が合わない原因の調査で遠回りしがちです。

実行順序を確認する方法はありますか?

php artisan route:listに-vを付けると、各ルートへ実際に割り当てられたクラスが一覧で出ます。グループ経由で入ったものも展開されるので、想定と違う組み合わせになっていないかをここで突き合わせる方法が確実です。グローバル側は一覧に出ないため、bootstrap/app.phpの記述と既定スタックを併せて確認します。

13系へ上げるとCSRFの設定は書き直しが要りますか?

すぐには要りません。validateCsrfTokensは非推奨の注記が付いただけで、実装としては新しいメソッドへ委譲されているので動作は変わらないためです。ただし将来の削除に備え、触る機会があればpreventRequestForgeryへ書き換えておくと安全です。オリジン検証の挙動を調整したい場合は、新しいメソッドの引数を使う必要があります。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.16 テックブログ バーガーキング不正アクセスで約1,118万件の情報漏洩|対象確認・案内メールの真偽と運営側のログ点検
  2. 2026.10.15 テックブログ 銀のさら不正アクセス:最大650万件の対象確認と窓口、パスワード変更の手順
  3. 2026.10.16 テックブログ ソースネクスト不正アクセスで最大2,252万件流出|ログインできない理由・パスワード再設定と会員APIの点検
  4. 2025.12.02 テックブログ クーパン問題とは?3,755万人流出と過去最大6,246億ウォンの課徴金
  5. 2026.10.15 テックブログ リゾートトラスト不正アクセス:会員2.6万件・カハラ横浜3.6万件の対象確認と窓口

RELATED POSTS 関連記事

目次