Skip to content
OpenCloseEsc
Tips
  • Laravel
  • rate-limiting
  • http

Prefix the by() keys when one limiter returns several limits

Three limits with the same by() key share one counter: the hour limit fills up at the minute's pace. A prefix per window keeps the counters apart.

1 min read

A limiter can return a list, which is how you get "60 a minute, 1,000 an hour, 10,000 a day" without writing a middleware. The catch is the cache key: it is built from the by() value, so three limits keyed by the same user id count in the same bucket.

RateLimiter::for('api', function (Request $request) {
    $id = $request->user()?->id ?: $request->ip();
 
    return [
        Limit::perMinute(60)->by('minute:'.$id),
        Limit::perHour(1000)->by('hour:'.$id),
        Limit::perDay(10000)->by('day:'.$id),
    ];
});

With the prefixes each window has its own counter and its own Retry-After. Without them the first request that hits any ceiling answers 429 for all three.

One more thing to know here: Limit::perMinutes(5, 100) takes the minutes first and the count second, the opposite of the other helpers.