Middleware in Laravel
Middleware provides a mechanism for filtering HTTP requests before they reach your controller. Think of middleware as layers of an onion — each request passes through them sequentially.
Creating Middleware
php artisan make:middleware EnsureUserIsAdmin
class EnsureUserIsAdmin
{
public function handle(Request $request, Closure $next): Response
{
if ($request->user()?->role !== 'admin') {
abort(403, 'Access denied.');
}
return $next($request);
}
}
Registering Middleware
In bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
// Global middleware (every request)
$middleware->append(TrackPageViews::class);
// Alias for route-level use
$middleware->alias([
'admin' => EnsureUserIsAdmin::class,
'subscribed' => EnsureUserIsSubscribed::class,
]);
})
Applying to Routes
Route::get('/admin', [AdminController::class, 'index'])
->middleware('admin');
Route::middleware(['auth', 'admin'])->group(function () {
Route::resource('/users', UserController::class);
});
Rate Limiting
Configure rate limiting in AppServiceProvider:
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(
$request->user()?->id ?: $request->ip()
);
});
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by(
$request->input('email') . $request->ip()
);
});
}
Route::post('/login', [AuthController::class, 'login'])
->middleware('throttle:login');
After Middleware
Middleware can also act on the response after the controller runs:
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// Add security headers
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-Content-Type-Options', 'nosniff');
return $response;
}
Middleware keeps cross-cutting concerns like authentication, rate limiting, and CORS separate from your business logic.