[ GUIDE ]

Monitoring rate limits in Laravel

Track 429s, identify misbehaving consumers, and use rate-limit state as an early-warning signal for attacks and bad integrations.

QUICK ANSWER

How do I monitor rate limits in Laravel?

Track 429 response rate per route in your APM. Laravel's throttle middleware returns 429 when limits are hit — a spike indicates a misbehaving consumer, bot, or attack. Drill in by IP, API key, or user_id to find the source. For distributed setups across multiple servers, use Redis as the rate limiter cache store so limits are enforced globally, not per-server. NightOwl surfaces 429s as part of its request monitoring; filter by status code.

Updated · 2026-04-13

Setting up Laravel rate limits

app/Providers/AppServiceProvider.php

php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

public function boot(): void
{
    RateLimiter::for('api', function ($request) {
        $key = $request->header('X-Api-Key') ?? $request->ip();
        return Limit::perMinute(100)->by($key);
    });

    RateLimiter::for('login', function ($request) {
        return [
            // 5 per minute per IP
            Limit::perMinute(5)->by($request->ip()),
            // 20 per minute per email (prevents targeted stuffing)
            Limit::perMinute(20)->by($request->input('email')),
        ];
    });
}

routes/api.php

php
Route::middleware('throttle:api')->group(function () {
    Route::get('/users', [UserController::class, 'index']);
    Route::post('/orders', [OrderController::class, 'store']);
});

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

Track 429s in your APM

429 responses show up in your request telemetry. The value is per-route grouping:

  • /api/* 429 spike → a consumer's retry logic is broken, or an API key is compromised
  • /login 429 spike → credential stuffing attack
  • /password/reset 429 spike → someone farming password reset emails
  • /api/webhooks/* 429 spike → your upstream is retrying because processing is slow

Log rate-limit hits with context

Custom middleware that logs 429s

php
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class LogRateLimitHits
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);

        if ($response->status() === 429) {
            Log::warning('Rate limit hit', [
                'route' => $request->route()?->uri(),
                'method' => $request->method(),
                'ip' => $request->ip(),
                'api_key' => $request->header('X-Api-Key'),
                'user_id' => auth()->id(),
                'limit_after' => $response->headers->get('X-RateLimit-Remaining'),
            ]);
        }

        return $response;
    }
}

Distributed rate limiting with Redis

On a multi-server fleet, Laravel's default cache-store rate limiter doesn't sync across servers — each server has its own counter:

.env

bash
CACHE_STORE=redis
REDIS_HOST=your-redis-host
REDIS_PORT=6379

# Optional: use a dedicated connection for rate limits
# so they're not evicted by general cache pressure:
# CACHE_RATE_LIMITER_CONNECTION=ratelimit

With Redis, increment+check operations are atomic and global. 100 req/min on a 3-server fleet means 100 total, not 300.

Alerting thresholds

Route pattern Alert at Likely cause
/login10+ 429s/minCredential stuffing
/api/*5x baseline sustained 10minRunaway client retries
/password/*3+ 429s/minPassword-reset farming
/api/webhooks/*any sustainedUpstream retry storm from slow processing

THE EASY WAY

NightOwl surfaces 429s as part of request monitoring

Filter the requests dashboard by status=429 per route. Drill into specific rate-limited requests to see which IP, API key, or user hit the ceiling. Set alerts on 429 rate thresholds via any configured alert channel.

bash
composer require nightowl/agent
php artisan nightowl:install

Frequently asked questions

How do I monitor rate limits in Laravel?

Laravel's RateLimiter returns 429 responses when limits are exceeded. Track the 429 rate per route in your APM. A spike usually indicates a specific consumer retrying aggressively or a bot; drill into the rate-limited requests by IP, API key, or user_id to find the source.

What's the difference between Laravel's throttle middleware and the RateLimiter facade?

The throttle middleware (Route::middleware('throttle:60,1')) is the high-level HTTP-routing use case — 60 requests per minute per keyed user. The RateLimiter facade is the programmatic API you use in controllers, jobs, or anywhere else you want to enforce a limit outside HTTP. Both ultimately use the same underlying Illuminate\Cache\RateLimiter.

How do I rate-limit per API key instead of per IP?

In a service provider, register a named limiter keyed by the API key: RateLimiter::for('api', fn($request) => Limit::perMinute(100)->by($request->header('X-Api-Key') ?? $request->ip())). Then apply via throttle:api in your route group.

Should I alert on high 429 rates?

Yes, but at the right threshold. A few 429s per minute are normal (someone spamming a form). Alert when 429 rate jumps 5x+ above baseline for 10+ minutes — that's a misbehaving integration or attack. Route-level alerts beat global: a spike on /api/login means credential stuffing, on /api/webhooks/stripe means your webhook handler is slow.

How do I know if my rate limits are too aggressive?

Track rate-limited users/keys. If the same legitimate user or API key repeatedly hits your limits, they're either being throttled too hard or you're missing proper per-tier quotas. Look at the distribution — if 99% of traffic is well under the limit and 1% is being limited, the limits are probably fine. If 30% is being limited, they're too strict.

Can I use Redis for distributed rate limiting in Laravel?

Yes — set CACHE_STORE=redis and the RateLimiter uses Redis atomically. This is required if you have multiple web servers, otherwise each server has its own in-memory counter and limits become per-server (effectively 3x more permissive on a 3-server fleet).

How do I communicate rate-limit state to API consumers?

Laravel includes X-RateLimit-Limit and X-RateLimit-Remaining headers automatically on throttled routes. Well-behaved clients read these and back off. Also return Retry-After on 429 responses. Document these headers in your API docs so consumers can build proper backoff into their SDKs.

PRICING

Flat pricing. No event caps. No per-seat fees.

14-day free trial, no credit card. Your PostgreSQL, your data.

HOBBY

$5 /month

1 app · 14 days lookback · all Laravel events

TEAM

$15 /month

Up to 3 connected apps · unlimited environments · all Laravel events

AGENCY

$69 /month

Unlimited apps · unlimited agent instances · same flat rate at any traffic

Related