Last updated: July 2026 — registration updated for Laravel 11/12 (bootstrap/app.php)
Middleware is a filter every HTTP request passes through before (or after) reaching your controller. Authentication, CSRF verification, CORS — all middleware. Think of it as airport security layers between the entrance (request) and the gate (controller).
Create one
php artisan make:middleware EnsureUserIsSubscribed
<?php
// app/Http/Middleware/EnsureUserIsSubscribed.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsSubscribed
{
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()?->subscribed) {
return redirect()->route('pricing');
}
return $next($request); // pass the request onward
}
}
Code before $next($request) runs before your controller; to act after the response is built:
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'DENY');
return $response;
}
Register it — this changed in Laravel 11
There is no app/Http/Kernel.php anymore. Registration lives in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
// alias for use on routes
$middleware->alias([
'subscribed' => \App\Http\Middleware\EnsureUserIsSubscribed::class,
]);
// or run on every request
$middleware->append(\App\Http\Middleware\SecurityHeaders::class);
// or add to the web group
$middleware->web(append: [\App\Http\Middleware\TrackLastActivity::class]);
})
(On Laravel 10 or older, the same three registrations go in Kernel.php: $middlewareAliases, $middleware, $middlewareGroups.)
Use it on routes
Route::get('/dashboard', DashboardController::class)->middleware('subscribed');
Route::middleware(['auth', 'subscribed'])->group(function () {
Route::get('/reports', [ReportController::class, 'index']);
});
Middleware parameters
Extra route arguments arrive after $next:
public function handle(Request $request, Closure $next, string $role): Response
{
abort_unless($request->user()?->hasRole($role), 403);
return $next($request);
}
Route::get('/admin', AdminController::class)->middleware('role:admin');
When middleware is the right tool
Cross-cutting checks tied to requests: auth and roles, locale switching, forcing HTTPS, logging, rate limiting, response headers. If the logic is business-rule-shaped (“can this user edit this post?”), use a Policy instead — middleware guards the road, policies guard the data.
