Laravel Tutorial · Requests and Responses · Lesson 11 of 27

Laravel Middleware

Run code before and after requests: authentication checks, logging, headers and rate limits. Write your own middleware and apply it to routes and groups.

Create a free account to save this page, take notes and track your progress. Sign in

What middleware does

Middleware sits between the incoming request and your route, like layers of an onion. Each layer can:

  • stop the request (return a 401 or redirect) before it reaches the route,
  • change the request (add data),
  • change the response on the way out (add headers).

Laravel ships with middleware for authentication (auth), throttling (throttle:60,1), signed URLs, CSRF protection and more. Create your own with php artisan make:middleware EnsureApiKey; a middleware has a handle($request, Closure $next) method that calls $next($request) to continue.

Apply middleware to a route with ->middleware(...), to a group with Route::middleware([...])->group(...), or globally in bootstrap/app.php with ->withMiddleware(...).

Custom middleware and rate limiting
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

class EnsureApiKey
{
    public function handle(Request $request, Closure $next)
    {
        if ($request->header('X-Api-Key') !== 'secret-key') {
            return response()->json(['message' => 'Invalid API key'], 401);   // stop here
        }
        $request->attributes->set('client', 'mobile-app');                    // pass data along
        return $next($request);
    }
}

class AddHandledByHeader
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);                    // the route runs here
        $response->headers->set('X-Handled-By', 'laravel');
        return $response;                               // change the response on the way out
    }
}

Route::middleware([AddHandledByHeader::class, EnsureApiKey::class])->group(function () {
    Route::get('/reports', fn (Request $request) => ['client' => $request->attributes->get('client')]);
});
Route::get('/ping', fn () => 'pong')->middleware('throttle:2,1');

visit('GET', '/reports');
$response = visit('GET', '/reports', [], ['X-Api-Key' => 'secret-key']);
echo 'X-Handled-By: ', $response->headers->get('X-Handled-By'), "\n";
foreach (range(1, 3) as $i) {
    visit('GET', '/ping');
}

Output

GET /reports -> 401 {"message":"Invalid API key"}
GET /reports -> 200 {"client":"mobile-app"}
X-Handled-By: laravel
GET /ping -> 200 pong
GET /ping -> 200 pong
GET /ping -> 429 {
    "message": "Too Many Attempts."
}

Run this on your computer — see “Get Started” for setup.

Rate limiting: ->middleware('throttle:3,1') allows 3 requests per minute per user/IP and answers 429 after that, with Retry-After headers.

Test yourself

In middleware, what does calling $next($request) do?

  1. Skips the route
  2. Passes the request to the next layer (eventually the route) and returns its response
  3. Restarts the request
  4. Logs the request
Show answer

Answer: Passes the request to the next layer (eventually the route) and returns its response

Was this page helpful?