
Laravel Middleware: Guardians of Your Application
Luigi Laezza
5 minutes
The Middleware Pipeline: Laravel treats middleware as a pipeline. Requests pass through them in the order they are registered, both on their way in and out of the application.
Request Processing: Middleware can access and modify the incoming Illuminate\Http\Request object, enabling manipulation of headers, query parameters, request body, and more.
Response Manipulation: Middleware can also access and modify the outgoing Illuminate\Http\Response object before it's sent to the client, affecting status codes, headers, and the response content.
Termination: Middleware can choose to terminate the request cycle by returning a response, preventing subsequent middleware or the route handler from executing.
Global, Route, and Group Middleware:Laravel allows you to apply middleware at different levels, providing flexibility to match your application's needs.
Simplified Skeleton: Laravel 11's streamlined application structure has slightly changed the directory structure, but the way middleware is defined and registered stays largely the same.
kernel.php: The kernel still plays a key role, with App\Http\Kernel containing the middleware setup.
Before Middleware: These execute before the route handler is triggered, acting like pre-processors on the request.
After Middleware (Terminable): These execute after the route handler and the response has been generated, making them ideal for post-processing. These need to implement \Illuminate\Contracts\Foundation\TerminableMiddleware.
php artisan make:middleware CheckAge
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class CheckAge { /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { if ($request->query('age') < 18) { return response('You are too young', 403); // Terminating the request } return $next($request); // Pass on to next middleware or the route handler } }
handle(Request $request, Closure $next): This is the main method where the middleware logic resides.
$request: The Illuminate\Http\Requestobject containing information about the incoming request.
$next: A callable that passes the request on to the next middleware or route handler.
return $next($request): This sends the request to the next in line.
return response('...');: Returning a response immediately terminates the middleware pipeline and the route handler.
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; use Illuminate\Contracts\Foundation\TerminableMiddleware; class LogResponse implements TerminableMiddleware { 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 { // Log the status code and URL to a file file_put_contents( storage_path('logs/responses.log'), now()->toDateTimeString() . " - URL: " . $request->url() . ", Status: " . $response->getStatusCode() . "\n", FILE_APPEND ); } }
implements TerminableMiddleware: The class declares it as a terminable middleware.
terminate(Request $request, Response $response): Executes after the response is generated. You have both the request and the response object.
<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. * * These middleware are run during every request to your application. * * @var array<int, class-string|string> */ protected $middleware = [ \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Http\Middleware\HandleCors::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Http\Middleware\TrustProxies::class, ]; /** * The application's route middleware groups. * * @var array<string, array<int, class-string|string>> */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Http\Middleware\HandleCors::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Http\Middleware\VerifyCsrfToken::class, ], 'api' => [ \Illuminate\Http\Middleware\HandleCors::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api', ], ]; /** * The application's route middleware aliases. * * These middleware may be assigned to groups or used individually. * * @var array<string, class-string|string> */ protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'cache' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'CheckAge' => \App\Http\Middleware\CheckAge::class, 'LogResponse' => \App\Http\Middleware\LogResponse::class, ]; /** * The priority-sorted list of middleware. * * This forces non-global middleware to always run in the given list order. * * @var array<int, string> */ protected $middlewarePriority = [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\Authenticate::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, \App\Http\Middleware\LogResponse::class, ]; }
$middlewareGroups: Groups of middleware that can be applied to routes. 'web' and 'api' are the default.
$routeMiddleware: Aliases for middleware allowing for easy use on individual routes or groups. We've added our custom middleware 'CheckAge' and 'LogResponse' here.
$middlewarePriority: Specify the order in which middleware runs. This is to resolve some dependencies between middleware, for example, ShareErrorsFromSession must execute after StartSession, and VerifyCsrfToken after ShareErrorsFromSession to work correctly. In this example, we've added \App\Http\Middleware\LogResponse::class, that will run after the standard Laravel middleware, and also can read the responseobject for logging.
Global Middleware: Added to $middlewarein Kernel.php.
Route Middleware: Used within route definitions or groups:
// In routes/web.php Route::get('/adult-content', function () { return 'Welcome to the adult content!'; })->middleware('CheckAge'); Route::middleware('LogResponse')->get('/log-test', function(){ return 'log me'; }); Route::group(['middleware' => ['auth', 'verified']], function () { Route::get('/verified-dashboard', function() { return 'verified access'; }); });
Authentication Middleware (using Laravel's built-in middleware):
// routes/web.php Route::middleware('auth')->get('/profile', function () { return 'This page is only for authenticated users'; });
Authorization Middleware (custom):
// app/Http/Middleware/CheckUserRole.php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class CheckUserRole { public function handle(Request $request, Closure $next, string $role): Response { if (! auth()->check() || auth()->user()->role !== $role) { abort(403, 'Unauthorized for this role.'); } return $next($request); } }
Register the middleware alias in app/Http/Kernel.php:
'admin' => \App\Http\Middleware\CheckUserRole::class . ':admin',
Use it in routes:
// routes/web.php Route::middleware('admin')->get('/admin', function () { return 'Admin Panel'; });
Locale Middleware (handling language setting):
// app/Http/Middleware/SetLocale.php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; use Symfony\Component\HttpFoundation\Response; class SetLocale { public function handle(Request $request, Closure $next): Response { $locale = $request->query('locale'); if( $locale && in_array($locale, ['en', 'fr', 'es']) ) { App::setLocale($locale); } else { $locale = session('locale', config('app.locale')); App::setLocale($locale); } return $next($request); } }
'setLocale' => \App\Http\Middleware\SetLocale::class,
Route::middleware('setLocale')->get('/welcome', function(){ return 'Welcome to my application'; });
Middleware Parameters: Like in the authorization example above, you can pass parameters to your middleware via the colon syntax.
Route Groups: Combine middleware for related routes by using Route::group(['middleware' => [...], ...]);
Priority Ordering: The $middlewarePriority in Kernel.php allows you to prioritize execution of certain middleware. This ensures certain middleware executes in the right order.
Middleware for APIs: APIs often need specific middleware for authentication (e.g., API tokens, OAuth) and rate limiting.
Testing: Middleware should be thoroughly unit-tested to ensure proper functionality.
By using the tools provided by Laravel 11 you can build middleware that perfectly matches your application requirements.
Related Articles

Laravel from scratch on was EC2
By Vimuth Somarathna

Laravel 5.3 add multi-language functionality
By Luigi Laezza

Mailtrap with symfony, testing emails has never been so easy
By Luigi Laezza

Improve your social media strategy with Semrush
By Luigi Laezza

On SMASHINGMAGAZINE an introduction to building and sending HTML email for web developers
By Luigi Laezza

Inexpensive stage lamp server with Raspberry PI3 and Ubunto server
By Luigi Laezza

Secure your SSH connection disabling SSH password logins
By Luigi Laezza

Analizzare tecniche e strumenti per conquistare nuovi clienti e far crescere la tua attivitÃ
By Luigi Laezza