Copied!
Programming
Laravel
PHP

Laravel Middleware Complete Guide – Create, Register & Use Middleware

Laravel Middleware Complete Guide – Create, Register & Use Middleware
Shahroz Javed
Mar 13, 2026 . 130 views

What is Middleware?

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

Create a Middleware

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.

Before vs After Middleware

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).

Before Middleware

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);
}

After Middleware

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;
}

Register Middleware

Laravel has three types of middleware registration: global, group, and route.

Global Middleware

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

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,
    ],
];

Middleware Aliases

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.

Apply Middleware 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);

Apply to a Group of Routes

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']);
});
💡 Related: Check out our guide on Laravel Routing – A Complete Beginner's Guide to understand how routes work before applying middleware.

Exclude Middleware

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);
});
⚠️ Note: withoutMiddleware() only works for route middleware. It cannot remove global middleware from a specific route.

Middleware Parameters

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');
}

Terminable Middleware

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());
}
⚠️ Note: For terminable middleware to work correctly, you should register it as a singleton in your 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);
}

Conclusion

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

📑 On This Page