Reader Stacks

What Is Laravel Middleware?

Middleware runs between a request arriving and a controller handling it — the mechanism behind auth checks, CORS headers, and rate limiting, all inspecting or modifying the request/response without the controller knowing.

Middleware sits between an incoming HTTP request and the controller that ultimately handles it — a filtering and modification layer that can inspect the request, reject it outright (redirecting to a login page, returning a 429 rate-limit response), or pass it through, and can do the same to the response on the way back out.

1. A concrete example: the auth middleware

Route::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth');

Before DashboardController::index() ever runs, the auth middleware checks whether the current user is authenticated — if not, it redirects to the login page and DashboardController::index() never executes at all. The controller itself contains no authentication-checking code; that concern is handled entirely by the middleware layer sitting in front of it.

2. Writing custom middleware

php artisan make:middleware EnsureUserIsAdmin
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureUserIsAdmin
{
    public function handle(Request $request, Closure $next)
    {
        if (! $request->user()?->is_admin) {
            abort(403, 'Admins only.');
        }

        return $next($request);
    }
}

$next($request) is what actually passes the request further down the chain — toward either the next middleware in line, or the controller itself once every middleware has run. Not calling $next() at all (as with the abort() call above, which halts execution) stops the request right there, and the controller never runs.

3. Registering and applying it

// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class,
    ]);
})
Route::get('/admin/dashboard', [AdminController::class, 'index'])->middleware(['auth', 'admin']);

Multiple middleware entries run in the order listed — here, auth runs first (confirming the user is logged in at all), then admin (confirming that logged-in user specifically has admin privileges). Order matters when one middleware's logic depends on another having already run — checking admin status before confirming a user is even authenticated wouldn't make sense.

4. Modifying the response on the way back out

public function handle(Request $request, Closure $next)
{
    $response = $next($request); // let the request continue through the chain first

    $response->headers->set('X-Custom-Header', 'value');

    return $response;
}

Code placed after the $next($request) call runs on the way back out, once the controller (and any deeper middleware) has already produced a response — this is how middleware can modify an outgoing response, adding headers or logging its status, rather than only intercepting the incoming request.

5. Global vs. route-specific middleware

// bootstrap/app.php — runs on literally every request, no route assignment needed
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\App\Http\Middleware\TrackPageViews::class);
})

Global middleware runs on every single request regardless of route — appropriate for something that genuinely applies app-wide (basic logging, a maintenance-mode check). Anything that should only apply to specific routes belongs as route-specific middleware instead, applied explicitly with ->middleware() the way auth is above — applying route-specific logic globally just means adding conditional logic inside the middleware to skip most requests, which is usually the wrong layer for that decision.

6. Common built-in middleware worth knowing

auth          // requires an authenticated user
guest         // requires the OPPOSITE — an unauthenticated visitor (used on login/register routes)
throttle:60,1 // rate limits to 60 requests per minute
verified      // requires a verified email address

Several of these ship with Laravel by default and cover the most common request-filtering needs directly — custom middleware is generally for application-specific logic (like the admin check above) that doesn't correspond to one of these standard, already-solved cases.

Topics: Authentication & Access Control