0

Rate limiting routes — throttle middleware, RateLimiter facade

Intermediate5 min read·lv-06-012
securitylaravel-src

Concept

Rate limiting routes controls how many requests a client can make to specific routes within a time window. Laravel provides the throttle middleware and the RateLimiter facade for flexible, named rate limiters.

throttle:N,M middleware: throttle:60,1 — 60 requests per 1 minute per IP. The simplest form. Can be applied to individual routes or groups.

Named rate limiters: Define complex rate limiting logic in AppServiceProvider::boot() (or RouteServiceProvider::boot()) using RateLimiter::for('name', callback). Apply with throttle:name.

RateLimiter::for callback: Receives a Request and returns a Limit or array of Limits. Limit::perMinute(N) — N requests per minute. Limit::perHour(N) — N requests per hour. Limit::perDay(N) — N requests per day. .by($key) — differentiate rate limits by user ID, IP, or any string.

Multiple limits: Return an array of Limit objects — all limits must be satisfied. [Limit::perMinute(10)->by($request->ip()), Limit::perMinute(20)->by($request->user()?->id ?? $request->ip())] — IP-based AND user-based limits simultaneously.

Custom response: ->response(fn() => response('Too many requests', 429)).

Headers: When throttle middleware is applied, responses include X-RateLimit-Limit, X-RateLimit-Remaining, and Retry-After headers — standard rate limit signaling for API clients.

Code Example

php
<?php
// In AppServiceProvider::boot() or RouteServiceProvider::boot()
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function(\Illuminate\Http\Request $request) {
    return Limit::perMinute(60)
        ->by($request->user()?->id ?: $request->ip());
});

RateLimiter::for('uploads', function(\Illuminate\Http\Request $request) {
    return [
        Limit::perMinute(5)->by($request->user()?->id ?: $request->ip()),
        Limit::perDay(50)->by($request->user()?->id ?: $request->ip()),
    ];
});

RateLimiter::for('login', function(\Illuminate\Http\Request $request) {
    return Limit::perMinute(5)
        ->by($request->input('email') . '|' . $request->ip())
        ->response(fn() => response()->json([
            'message' => 'Too many login attempts. Please try again in 60 seconds.',
        ], 429));
});

// Apply rate limiters in routes
Route::middleware('throttle:api')->group(function() {
    Route::apiResource('products', ProductController::class);
    Route::apiResource('orders', OrderController::class);
});

Route::post('/login', LoginController::class)->middleware('throttle:login');
Route::post('/upload', UploadController::class)->middleware('throttle:uploads');

// Simple inline rate limit (60 requests per minute per IP)
Route::get('/search', SearchController::class)->middleware('throttle:60,1');

// Route group with rate limiting
Route::prefix('api/v1')
    ->middleware(['auth:sanctum', 'throttle:api'])
    ->group(function() {
        Route::get('/user', fn() => auth()->user());
        Route::post('/orders', [OrderController::class, 'store']);
    });