API rate limiter returns 429 without Retry-After header
Medium
staging
Laravel
API
4
views
2
votes
The API rate limiting middleware rejects requests with a 429 Too Many Requests response, but the response does not include the standard Retry-After header. Clients that rely on this header to implement backoff logic retry immediately and get banned.
Steps to reproduce
- Enable the default throttle middleware on any API route (
throttle:60,1) - Send more than 60 requests within one minute
- Inspect the 429 response headers —
Retry-Afteris missing
Expected behavior
The 429 response should include a Retry-After header indicating how many seconds the client must wait before retrying.
A
abdelhamid
Jul 28, 2026
Solutions
1
2
A
abdelhamid
Jul 28, 2026
The ThrottleRequests middleware only adds the Retry-After header when the X-RateLimit-* headers are enabled in its constructor. Make sure the throttle middleware is registered with headers enabled, or add the header manually in the exception handler.
// app/Http/Kernel.php
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
// app/Exceptions/Handler.php — fallback: add the header manually
$this->renderable(function (ThrottleRequestsException $e, $request) {
return response()->json(['message' => 'Too many requests'], 429)
->header('Retry-After', $e->getHeaders()['Retry-After'] ?? 60);
});
After this change, curl -i on a throttled route shows Retry-After: 60 in the 429 response.
Have a solution to share?
Sign in to submit a solution