What are you looking for?
What are you looking for?
Middleware in Laravel acts as a bridge between an incoming HTTP request and your application. Think of it as a filter — every request that enters your app can pass through one or more middleware layers before it reaches the route or controller.
Common real-world uses of middleware include:
Checking if a user is authenticated before accessing a page
Logging every incoming request
Adding CORS headers to API responses
Rate limiting requests to prevent abuse
Laravel makes it easy to generate middleware using Artisan. Run the following command in your terminal:
php artisan make:middleware EnsureUserIsActive
This creates a new file at app/Http/Middleware/EnsureUserIsActive.php.
The generated class looks like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class EnsureUserIsActive
{
public function handle(Request $request, Closure $next)
{
if (! $request->user() || ! $request->user()->is_active) {
return redirect('/inactive');
}
return $next($request);
}
}
The handle() method receives the incoming request and a $next closure.
If your condition passes, call $next($request) to pass the request deeper into the application.
Otherwise, return a redirect or response to stop the request.
Middleware can perform logic either before or after the request is handled.
This is controlled by where you place your logic relative to $next($request).
Code runs before the request reaches the controller:
public function handle(Request $request, Closure $next)
{
// Logic runs BEFORE the request is handled
logger('Incoming request: ' . $request->url());
return $next($request);
}
Code runs after the response is generated but before it's sent to the browser:
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// Logic runs AFTER the response is generated
$response->headers->set('X-App-Version', '1.0');
return $response;
}
Laravel has three types of middleware registration: global, group, and route.
Global middleware runs on every single HTTP request to your application.
In Laravel 11, you register global middleware in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
$middleware->append(EnsureUserIsActive::class);
})
In Laravel 10 and below, you register it in the $middleware array inside app/Http/Kernel.php:
protected $middleware = [
// ...
\App\Http\Middleware\EnsureUserIsActive::class,
];
Middleware groups let you bundle multiple middleware under a single key, making them easy to apply together.
Laravel ships with two built-in groups: web and api, which are automatically applied to your routes.
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// ...
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
Instead of using the full class name every time, you can give your middleware a short alias:
protected $middlewareAliases = [
'active' => \App\Http\Middleware\EnsureUserIsActive::class,
'role' => \App\Http\Middleware\CheckRole::class,
];
Now you can use 'active' instead of the full class name when assigning to routes.
You can assign middleware to a specific route directly in your routes file using the middleware() method:
// Single middleware
Route::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth');
// Multiple middleware
Route::get('/admin', [AdminController::class, 'index'])->middleware(['auth', 'active']);
// Using the full class name
Route::get('/profile', [ProfileController::class, 'show'])->middleware(EnsureUserIsActive::class);
You can also apply middleware to a group of routes at once:
Route::middleware(['auth', 'active'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::get('/profile', [ProfileController::class, 'show']);
Route::get('/settings', [SettingsController::class, 'index']);
});
Sometimes you apply middleware to a group of routes, but want to exclude it for one specific route.
Use withoutMiddleware() to do this:
Route::middleware(['auth', 'active'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
// This route skips the 'active' check
Route::get('/reactivate', [AccountController::class, 'reactivate'])
->withoutMiddleware(EnsureUserIsActive::class);
});
withoutMiddleware() only works for route middleware. It cannot remove global middleware from a specific route.
Middleware can also accept additional parameters. This is very useful for role-based access control.
For example, let's create a CheckRole middleware that accepts a role name as a parameter:
public function handle(Request $request, Closure $next, string $role)
{
if (! $request->user()->hasRole($role)) {
return redirect('/unauthorized');
}
return $next($request);
}
You pass parameters to middleware using a colon : after the middleware name, with multiple parameters separated by commas:
// Single parameter
Route::get('/editor', [EditorController::class, 'index'])->middleware('role:editor');
// Multiple parameters
Route::get('/super', [SuperController::class, 'index'])->middleware('role:admin,editor');
And update the handle() method to accept multiple roles:
public function handle(Request $request, Closure $next, string ...$roles)
{
foreach ($roles as $role) {
if ($request->user()->hasRole($role)) {
return $next($request);
}
}
return redirect('/unauthorized');
}
Sometimes you need to do work after the HTTP response has already been sent to the browser — for example, writing session data to a database or sending analytics. This is where terminable middleware comes in.
Add a terminate() method to your middleware class:
public function terminate(Request $request, $response)
{
// Runs after the response is sent to the browser
logger('Response sent for: ' . $request->url());
}
AppServiceProvider so Laravel uses the same instance for both handle() and terminate().
// In App\Providers\AppServiceProvider.php
public function register()
{
$this->app->singleton(\App\Http\Middleware\LogAfterResponse::class);
}
Laravel Middleware is one of the most powerful and flexible features of the framework. Whether you're protecting routes, logging requests, setting headers, or running post-response tasks — middleware gives you a clean, reusable way to handle all of it.
Here's a quick summary of what we covered:
Use php artisan make:middleware to generate a middleware class
Place logic before or after $next($request) to control when it runs
Register middleware as global, group, or route-level
Use ->middleware() to assign and ->withoutMiddleware() to exclude
Pass dynamic values using middleware parameters with the role:editor syntax
Use terminable middleware for post-response tasks