ミドルウェア
リクエストがアプリに届く前後に間に入って確かめる、ミドルウェアの作り方・登録のしかた・グループ・別名・パラメータ・後始末の処理を説明します。
ミドルウェアは、ブラウザから来たリクエスト(お願い)が、アプリの本体に届く前に通る「関所」のような処理です。リクエストを調べて、通すか、止めるかを決められます。たとえば、Laravel にはログインしているかを確かめるミドルウェアがあります。ログインしていなければログイン画面へ送り、していれば先へ通します。
ほかにも、届いたリクエストをすべて記録するミドルウェアなど、いろいろ作れます。認証(ログインして「だれか」を確かめること)や CSRF 対策のミドルウェアは、Laravel が最初から持っています。自分で作ったミドルウェアは、ふつう app/Http/Middleware フォルダに置きます。
ミドルウェアを作る#
make:middleware という Artisan コマンド(php artisan で動かす Laravel のコマンド)で、ひな形ができます。
php artisan make:middleware EnsureTokenIsValid
app/Http/Middleware に EnsureTokenIsValid クラスができます。次の例は、送られてきた token が決めた値と合うときだけ通し、合わなければ /home へ転送します。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureTokenIsValid
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->input('token') !== 'my-secret-token') {
return redirect('/home');
}
return $next($request);
}
}
token が合わなければ、転送のレスポンス(サーバーが返す答え)を返して、そこで止めます。合えば、$next に $request を渡して呼びます。これが、リクエストをアプリのさらに奥へ通す(関所を通す)合図です。
ミドルウェアは、HTTP リクエストが本体に届くまでに通る何枚もの「層」だと考えると分かりやすいです。層ごとにリクエストを調べて、通さないこともできます。
補足
ミドルウェアはサービスコンテナ(クラスを作って渡してくれる道具箱)から作られます。だから、必要な部品は、コンストラクタ(クラスを作るときに動く部分)に型を書けば受け取れます。
リクエストの前と後に処理する#
ミドルウェアは、リクエストを奥へ渡す前にも、渡したあとにも、処理を書けます。次のミドルウェアは、アプリがリクエストを処理する前に仕事をします。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class BeforeMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// 処理を書く
return $next($request);
}
}
こちらは、アプリがリクエストを処理した後に仕事をします。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class AfterMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// 処理を書く
return $response;
}
}
ミドルウェアを登録する#
作っただけでは動きません。どこに使うかを決めて登録します。
すべてのリクエストにかける(グローバルミドルウェア)#
すべての HTTP リクエストで動かしたいときは、bootstrap/app.php で、全体のミドルウェアの並びに足します。
use App\Http\Middleware\EnsureTokenIsValid;
->withMiddleware(function (Middleware $middleware): void {
$middleware->append(EnsureTokenIsValid::class);
})
withMiddleware のクロージャ(名前のない関数)には、$middleware が渡されます。これは Illuminate\Foundation\Configuration\Middleware クラスの実体で、ルートにかかるミドルウェアをまとめて管理する係です。append は並びの最後に足します。最初に足したいときは prepend を使います。
標準の全体ミドルウェアを自分で管理する#
Laravel が最初から入れている全体のミドルウェアを、自分で管理したいときは、標準の一覧を use メソッドに渡します。そこから、必要に応じて足したり減らしたりします。
->withMiddleware(function (Middleware $middleware): void {
$middleware->use([
\Illuminate\Http\Middleware\ValidatePathEncoding::class,
\Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
// \Illuminate\Http\Middleware\TrustHosts::class,
\Illuminate\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Http\Middleware\ValidatePostSize::class,
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
]);
})
特定のルートにかける#
一部のルートだけにかけたいときは、ルートに middleware メソッドをつなぎます。
use App\Http\Middleware\EnsureTokenIsValid;
Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);
配列で渡せば、複数かけられます。
Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);
ミドルウェアを外す#
ルートのグループにミドルウェアをかけたとき、グループの中の1つのルートだけ外したいことがあります。withoutMiddleware を使います。
use App\Http\Middleware\EnsureTokenIsValid;
Route::middleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/', function () {
// ...
});
Route::get('/profile', function () {
// ...
})->withoutMiddleware([EnsureTokenIsValid::class]);
});
ルートグループ全体から外すこともできます。
use App\Http\Middleware\EnsureTokenIsValid;
Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/profile', function () {
// ...
});
});
補足
withoutMiddleware で外せるのは、ルートにかけたミドルウェアだけです。全体にかけたミドルウェアは外せません。
ミドルウェアのグループ#
いくつかのミドルウェアを、1つの名前でまとめておくと、ルートへ付けるのが楽になります。bootstrap/app.php で appendToGroup を使います。
use App\Http\Middleware\First;
use App\Http\Middleware\Second;
->withMiddleware(function (Middleware $middleware): void {
$middleware->appendToGroup('group-name', [
First::class,
Second::class,
]);
$middleware->prependToGroup('group-name', [
First::class,
Second::class,
]);
})
グループは、1つのミドルウェアと同じ書き方で、ルートやコントローラーに付けられます。
Route::get('/', function () {
// ...
})->middleware('group-name');
Route::middleware(['group-name'])->group(function () {
// ...
});
最初からあるグループ#
Laravel には、web と api のグループが最初からあります。routes/web.php と routes/api.php のルートには、自動でかかります。
web グループのミドルウェアは次のとおりです。
| ミドルウェア | 説明 |
|---|---|
Illuminate\Cookie\Middleware\EncryptCookies |
Cookie(ブラウザに覚えさせる小さなデータ)を暗号化する |
Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse |
後で渡す予定の Cookie をレスポンスに付ける |
Illuminate\Session\Middleware\StartSession |
セッションを始める |
Illuminate\View\Middleware\ShareErrorsFromSession |
セッションのエラーをビューで使えるようにする |
Illuminate\Foundation\Http\Middleware\PreventRequestForgery |
CSRF 対策をする |
Illuminate\Routing\Middleware\SubstituteBindings |
URL の値からモデルを取り出す(ルートモデルバインディング) |
api グループのミドルウェアは次の1つです。
| ミドルウェア | 説明 |
|---|---|
Illuminate\Routing\Middleware\SubstituteBindings |
URL の値からモデルを取り出す(ルートモデルバインディング) |
この2つのグループに足したいときは、bootstrap/app.php の web と api メソッドを使います。appendToGroup の近道です。
use App\Http\Middleware\EnsureTokenIsValid;
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
EnsureUserIsSubscribed::class,
]);
$middleware->api(prepend: [
EnsureTokenIsValid::class,
]);
})
最初からあるものを、自作のものに差しかえることもできます。
use App\Http\Middleware\StartCustomSession;
use Illuminate\Session\Middleware\StartSession;
$middleware->web(replace: [
StartSession::class => StartCustomSession::class,
]);
まるごと外すこともできます。
$middleware->web(remove: [
StartSession::class,
]);
最初からあるグループを自分で管理する#
web と api の中身を全部自分で管理したいときは、グループを作り直します。次の例は、最初からある中身で作り直したものです。ここから好きに変えられます。
->withMiddleware(function (Middleware $middleware): void {
$middleware->group('web', [
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\PreventRequestForgery::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
]);
$middleware->group('api', [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
// 'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
]);
})
補足
web と api のグループは、bootstrap/app.php によって、routes/web.php と routes/api.php に自動でかかります。
別名(エイリアス)を付ける#
長いクラス名のミドルウェアには、短い別名を付けられます。bootstrap/app.php に書きます。
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'subscribed' => EnsureUserIsSubscribed::class
]);
})
別名を決めたら、ルートに付けるときに別名で書けます。
Route::get('/profile', function () {
// ...
})->middleware('subscribed');
Laravel のミドルウェアには、最初から別名が付いたものがあります。たとえば auth は Illuminate\Auth\Middleware\Authenticate の別名です。最初からある別名は次のとおりです。
| 別名 | 説明 |
|---|---|
auth |
ログインしているか確かめる(Illuminate\Auth\Middleware\Authenticate) |
auth.basic |
ベーシック認証でログインを確かめる(Illuminate\Auth\Middleware\AuthenticateWithBasicAuth) |
auth.session |
セッションでログインを確かめる(Illuminate\Session\Middleware\AuthenticateSession) |
cache.headers |
キャッシュのヘッダーを付ける(Illuminate\Http\Middleware\SetCacheHeaders) |
can |
してよいことか確かめる(Illuminate\Auth\Middleware\Authorize) |
guest |
ログイン済みなら別へ送る(Illuminate\Auth\Middleware\RedirectIfAuthenticated) |
password.confirm |
パスワードをもう一度入れてもらう(Illuminate\Auth\Middleware\RequirePassword) |
precognitive |
事前確認のリクエストを扱う(Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests) |
signed |
署名付き URL(書きかえを見破れる URL)を確かめる(Illuminate\Routing\Middleware\ValidateSignature) |
subscribed |
定期契約(サブスクリプション)中か確かめる(\Spark\Http\Middleware\VerifyBillableIsSubscribed) |
throttle |
回数を制限する(Illuminate\Routing\Middleware\ThrottleRequests か Illuminate\Routing\Middleware\ThrottleRequestsWithRedis) |
verified |
メールアドレスの確認が済んでいるか確かめる(Illuminate\Auth\Middleware\EnsureEmailIsVerified) |
動く順番を決める#
まれに、ミドルウェアを決まった順に動かしたいのに、ルートに付ける順番を自分では決められないことがあります。そのときは、bootstrap/app.php の priority メソッドで、優先する順番を決めます。
->withMiddleware(function (Middleware $middleware): void {
$middleware->priority([
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\PreventRequestForgery::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
\Illuminate\Auth\Middleware\Authorize::class,
]);
})
いまある優先の一覧を置きかえずに、足すだけにしたいときは、prependToPriorityList と appendToPriorityList を使います。
| メソッド | 説明 |
|---|---|
priority |
優先する順番の一覧を、まるごと決める |
prependToPriorityList |
指定したミドルウェアの前に、別のミドルウェアを入れる |
appendToPriorityList |
指定したミドルウェアの後に、別のミドルウェアを入れる |
->withMiddleware(function (Middleware $middleware): void {
$middleware->prependToPriorityList(
before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
prepend: \App\Http\Middleware\EnsureTokenIsValid::class,
);
$middleware->appendToPriorityList(
after: \Illuminate\Routing\Middleware\SubstituteBindings::class,
append: \App\Http\Middleware\EnsureUserIsSubscribed::class,
);
})
before と after には、ミドルウェアのクラスの配列も渡せます。
ミドルウェアにパラメータを渡す#
ミドルウェアには、追加の値(パラメータ)も渡せます。たとえば、ログイン中の人が、ある「役割(ロール)」を持つかを確かめたいなら、役割の名前を受け取る EnsureUserHasRole ミドルウェアが作れます。
追加の値は、$next のあとの引数で受け取ります。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string $role): Response
{
if (! $request->user()->hasRole($role)) {
// 転送などをする
}
return $next($request);
}
}
ルートに付けるときは、ミドルウェアの名前のあとに : を書き、その後ろにパラメータを書きます。
use App\Http\Middleware\EnsureUserHasRole;
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor');
パラメータが複数あるときは、カンマで区切ります。
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor,publisher');
レスポンスを返したあとに動かす(terminate)#
ブラウザへレスポンスを送ったあとに、仕事をしたいことがあります。ミドルウェアに terminate メソッドを書いておくと、Web サーバーが FastCGI(Web サーバーと PHP をつなぐしくみの1つ)を使っているとき、レスポンスを送ったあとに自動で呼ばれます。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class TerminatingMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
/**
* Handle tasks after the response has been sent to the browser.
*/
public function terminate(Request $request, Response $response): void
{
// ...
}
}
terminate は、リクエストとレスポンスの両方を受け取ります。作ったら、bootstrap/app.php で、ルートか全体のミドルウェアに足します。
terminate を呼ぶとき、Laravel はサービスコンテナから、新しいミドルウェアのインスタンス(実体)を作ります。handle と terminate で同じインスタンスを使いたいときは、コンテナの singleton メソッド(1つだけ作って使い回す登録のしかた)で登録します。ふつうは AppServiceProvider の register メソッドに書きます。
use App\Http\Middleware\TerminatingMiddleware;
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}
関連するページ#
公式ドキュメント(英語)
2026年10月5日時点の内容をもとに、日本語でまとめています。