What middleware is — the pipeline pattern
Concept
Middleware is software that sits between the HTTP request and the application response, intercepting requests (and responses) to apply cross-cutting concerns: authentication, logging, rate limiting, CORS headers, compression, locale detection.
The pipeline pattern: Laravel routes requests through a pipeline of middleware before they reach a controller. Each middleware in the pipeline either passes the request to the next middleware ($next($request)), or short-circuits and returns its own response. After the controller returns, the response passes back through the middleware stack in reverse order.
Metaphor: Think of middleware as airport security layers. The request is a passenger. Each checkpoint (middleware) can: let the passenger through, search their bag and add a stamp (modify request/response), or turn them away (short-circuit with a 401/403/429). After the flight (controller runs), the passenger (response) passes back through checkpoints on the way out (you can modify responses here too).
Middleware kinds:
- Request middleware: Inspect/modify request before passing to next.
- Response middleware: Inspect/modify response after getting it from next.
- Short-circuit middleware: Return a response without calling
$next(auth failure, rate limit exceeded). - Wrapping middleware: Do something before AND after (transaction wrapping, request timing).
Laravel's built-in middleware: App\Http\Middleware\Authenticate (auth), VerifyCsrfToken, TrimStrings, ConvertEmptyStringsToNull, TrustProxies, PreventRequestsDuringMaintenance, and HTTP middleware kernel stack.
Code Example
<?php
// Middleware pipeline visualization
// Incoming request → [TrustProxies] → [ThrottleRequests] → [Authenticate] → Controller → Response
// ← ← ← ←
// Minimal middleware structure
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ExampleMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// === BEFORE the controller runs ===
// Inspect/modify the incoming request here
$response = $next($request); // pass to next middleware (or controller)
// === AFTER the controller runs ===
// Inspect/modify the outgoing response here
return $response;
}
}
// Request-only middleware — add request context
class AddRequestId
{
public function handle(Request $request, Closure $next): Response
{
$requestId = (string) \Illuminate\Support\Str::uuid();
$request->headers->set('X-Request-ID', $requestId);
return $next($request);
}
}
// Response-only middleware — add response headers
class AddVersionHeader
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-API-Version', config('app.api_version'));
return $response;
}
}
// Short-circuit middleware — stop the request
class RequireHttps
{
public function handle(Request $request, Closure $next): Response
{
if (!$request->secure() && app()->isProduction()) {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
}