🥐

Laravel Middlewareの役割と使い方

に公開

📝 はじめに

こんにちは、レバテック開発部のみっつんです。
この記事は レバテック開発部 Advent Calendar 2025 4日目の記事です。

業務でMiddleware周りを調べるきっかけがあり、この時の学びをまとめました。
初学者やMiddlewareを使っているものの、その仕組みがよくわからないという方のお役に立てれば幸いです。

この記事でわかること

  • LaravelにおけるMiddlewareの役割
  • Middlewareの使い方
  • 実際の処理フロー

対象読者

  • Middlewareって何?という方
  • Middleware使っているけど、役割や裏側の処理が気になる方

🛠 筆者の環境

  • Laravel: 11.41
  • PHP: 8.2

この記事を書くことになった経緯

以下にこの記事を書くことになった発端と原因を記載しております。
(本題とは逸れるため「そもそもMiddlewareって?」まで飛ばしても大丈夫です。)

発端

  • 機能追加のためAPIを作成しており、動作確認を行なった際にレスポンスが401で返却された。
  • デバッグするとコントローラーまで到達していないことがわかった。
routes/api.php
use App\Http\Api\HogeController;

Route::group([
   'domain' => getenv('API_URL'),
   'middleware' => ['client_credentials'],
], function ($router) {
   Route::post('/hoge/address/is_exists', [HogeController::class, 'isExistsMailAddress']);
});
App\Http\Api\HogeController.php
<?php
namespace App\Http\Api;

class HogeController
{
    /**
     * メールアドレス重複確認API
     *
     * @param IsExistsMailAddressRequest $request
     * @return JsonResponse
     */
    public function isExistsMailAddress(IsExistsMailAddressRequest $request): JsonResponse
    {
        dd('コントローラーまで到達');
        //
        // 以後確認処理...
        //
    }
}

curlでリクエスト&レスポンス確認

% curl "{API_BASE_URL}/hoge/mail_address/is_exists" \ \ 
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {アクセストークン}" \
  -d '{"mail_address": "test@example.com"}' \
  -s -w "\nHTTP Status: %{http_code}\n"

{"message":"Unauthenticated"}
HTTP Status: 401

原因

APIの認証方式にBearerトークン認証を採用しており、リクエスト時のトークンがサーバー側が期待しているものと異なっていたため、Middlewareで無効なトークンと判定され、401が返却されていました。

イメージ図:認証フローの概要
イメージ

リクエスト

トークンを検証するMiddlewareで401が返却

app/Http/Middleware/CheckClientCredentials.php
class CheckClientCredentials extends CheckToken
{
    /**
     * Create a new middleware instance.
     */
    public function __construct(
    ) {
        parent::__construct($server);
    }

    /**
     * Handle an incoming request.
     * @inheritDoc
     */
    public function handle(Request $request, Closure $next, string ...$scopes): Response
    {
        try {
            // ①トークンを検証する
            $token = $this->validateToken($request);
            //
            // 以後確認処理...
            //
        } catch (AuthenticationException) {
            // ②無効なトークンとして401が返却される
            return response()->json(
                ['message' => 'Unauthenticated'],
                Response::HTTP_UNAUTHORIZED
            );
        }
    }
}

※周辺情報(Bearerとclient_credentials)については、ここでは触れないため関連する記事をご参照ください。


そもそもMiddlewareって?

本題です。
上述の事象に直面した際、コントローラーに到達する前にMiddlewareで様々な処理が行われていることが判明し、Middlewareの役割と使い方について調査しました。

役割

Laravel公式ドキュメントより以下抜粋

・アプリケーションに入力されるHTTPリクエストを検査およびフィルタリングする
・リクエストをアプリケーションのより深いレベルに渡す前または渡した後にタスクを実行することができる
・Laravelには、アプリケーションのユーザーが認証されていることを確認するミドルウェアが含まれる
・認証以外にも様々なタスクを実行するミドルウェアを追加で作成でき、例えばロギングなど、アプリケーションへのすべての受信リクエストをログに記録する

Middlewareの役割は主に以下であり、オフィスのセキュリティゲートのような身分確認や入退館の記録をするものだと理解しました。

  • HTTPリクエストがアプリケーション本体に到達する前にHTTPリクエストの検査およびフィルタリングを行う
  • アプリケーション本体の処理が完了した後、レスポンス返却前にHTTPレスポンスの調整および記録を行う

以下ではMiddlewareの「入館」にあたる部分(HTTPリクエストがアプリケーション本体に到達する前)の使い方を記載しています。


使い方

以下4つで構成されており要件に合わせて使い分けます。

1. グローバルミドルウェア

  • アプリケーション内すべてのHTTPリクエストで実行するミドルウェアを$middlewareに定義する
  • Webルート、APIルート、関わらず全てのルートで実行するミドルウェアを設定できる

2. ミドルウェアグループ

  • 各ルートで実行したいミドルウェアを$middlewareGroupsに定義する
  • WebルートやAPIルートなど、ルート単位で実行するミドルウェアを設定できる

3. ルートミドルウェア

  • 個々のエンドポイントで実行するミドルウェアを$middlewareAliasesに定義する
  • 特定のエンドポイントでのみ実行するミドルウェアを設定できる

4. ミドルウェアソート

  • ミドルウェアの実行順で$middlewarePriorityに定義する
  • 実行順序に依存するケースが発生した場合に実行順序を固定化できる
  • ※ミドルウェアグループ、ルートミドルウェアにのみ適用されます。

以下は設定例

app/Http/Kernel.php
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    // 1. グローバルミドルウェア
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    ];

    // 2. ミドルウェアグループ
    protected $middlewareGroups = [
        'web' => [
            \Illuminate\Cookie\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        ],

        'api' => [
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
        ],
    ];

    // 3. ルートミドルウェア
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

    // 4. ミドルウェアソート
    protected $middlewarePriority = [
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ];
}


以下のルーティングであれば

routes/api.php
use App\Http\Api\HogeController;

Route::post('/hoge/mail_address/is_exists', [HogeController::class, 'isExistsMailAddress'])
    ->middleware(['auth']);

コントローラーに到達する前に、1. グローバルミドルウェアのCheckForMaintenanceModeから順番に実行されます。

  1. グローバルミドルウェア
    • CheckForMaintenanceMode
    • ValidatePostSize
  2. ミドルウェアグループ
    • ThrottleRequests
    • SubstituteBindings
  3. ルートミドルウェア
    • Authenticate

補足

Middlewareのより詳細な処理フローについては「LaravelのMiddlewareはどのように動いているのか?」にて記載されているため、気になる方はこちらをご参照ください。

おわりに

ここまで読んでいただきありがとうございます。
調査、記事の作成を通して少しMiddlewareについて詳しくなれたと思う反面、ブラックボックスになっているフレームワークの処理を理解するのは難しいなと感じました。
つよつよエンジニアの方達は、日々どの様にして読み解いているのか気になるこの頃です。

参考にした記事

https://dexall.co.jp/articles/?p=2646#i-0
https://zenn.dev/yskn_sid25/articles/6bb62cbc02445f
https://laravel.com/docs/11.x/middleware
https://laravel.com/docs/10.x/middleware
https://laravel.com/docs/11.x/lifecycle

レバテック開発部

Discussion