Laravel Rate Limiting: Setting Sensible Defaults

Default rate limits in Laravel are easy to set and easy to set wrong. Here's a practical approach to per-route, per-user, and per-IP throttling.

Richard GamoraRichard GamoraFullstack developer·4 min read
LaravelSecurityAPI

Rate limiting is one of those things that everyone knows they should set up and most people set up wrong. Laravel makes it easy to attach a throttle middleware to a route, but the defaults are too generous and the keys are often wrong.

Per-IP is rarely what you want

The default throttle middleware uses the request IP as its key. For an authenticated app, this is wrong on two sides: shared offices share an IP, so legitimate users hit the limit; and a determined attacker rotates IPs cheaply.

For authenticated routes, key the limiter on the user ID. For public routes, key on a combination — IP plus a fingerprint, IP plus the route, or IP plus an email if it is a login attempt. The key should match the thing you are actually trying to limit.

Custom limiters in RouteServiceProvider

phpRateLimiter::for('api', function (Request $request) {
    return $request->user()
        ? Limit::perMinute(120)->by($request->user()->id)
        : Limit::perMinute(30)->by($request->ip());
});

RateLimiter::for('login', function (Request $request) {
    $email = (string) $request->input('email');
    return [
        Limit::perMinute(5)->by($email . $request->ip()),
        Limit::perDay(50)->by($request->ip()),
    ];
});

Returning multiple Limit objects gives you layered protection. The login example above limits attempts per email-IP combo (stops password spraying) and per IP (stops broad brute force) at the same time.

Tell the user what they hit

When a request gets rate-limited, return a 429 with a Retry-After header and a JSON body explaining the limit. "Too many requests" with no detail is hostile to clients who would happily slow down if they knew the rules. Limit::response() lets you customize this per limiter.

Sensible rate limits are not aggressive. They are explicit, well-keyed, and well-communicated.

About the author

Richard Gamora

Richard Gamora

Fullstack developer based in the Philippines, working mostly with Laravel and Vue.js, with eight years of production experience across web and mobile.

me@richardgamora.comUpwork ↗

More on Laravel