Quick answer
Rate limiting restricts how many requests a given client may make within a period of time, and rejects or delays anything beyond that. It is the simplest effective defence against brute-force login attempts, scraping, API abuse and small floods, because it makes each attempt cost the attacker time regardless of how the request is disguised. The hard parts are choosing what counts as a client and setting a limit that stops abuse without breaking legitimate bursts.
The common algorithms
Token bucket is the usual choice for public APIs because real clients are bursty — a page load may fire ten requests at once and then nothing for a minute. A strict per-second cap punishes that pattern; a bucket absorbs it while still limiting the sustained rate.
| Algorithm | How it behaves | Trade-off |
|---|---|---|
| Fixed window | Counts requests per calendar interval, resets at the boundary | Simple, but allows a double burst across the boundary |
| Sliding window | Counts over the trailing N seconds | Accurate, more state to keep |
| Token bucket | Tokens refill at a steady rate; each request spends one | Allows short bursts while capping the sustained rate |
| Leaky bucket | Requests queue and drain at a fixed rate | Smooths traffic, but adds latency instead of rejecting |
What to count per — the decision that matters most
A rate limit is only as good as its key. Limit per IP address and you punish everyone behind a shared connection: an office, a university, a mobile carrier's NAT, an entire country in some cases.
- Per IP — the default; fine for anonymous traffic, unfair to shared connections
- Per API key or account — much fairer, and possible wherever the client authenticates
- Per session — works for logged-in users, and defeats attackers by making them log in
- Per IP and endpoint — a tight limit on /login with a loose one everywhere else
- Per subnet — for attacks spread across many addresses in one network
Responding well when a limit is hit
The correct status is `429 Too Many Requests`, and it should be accompanied by a `Retry-After` header telling the client how long to wait. Well-written clients honour it and back off; without it they retry immediately and make the problem worse.
For public APIs, it is worth returning the limit state on every response rather than only on rejection, so clients can pace themselves before hitting the wall.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30What rate limiting cannot do
A rate limit assumes an attacker is constrained by how fast one client can go. A distributed attack removes that assumption: ten thousand addresses each making two requests a second will pass any sane per-client limit while delivering twenty thousand requests a second to your origin.
This is why rate limiting is a layer rather than a solution. It handles brute force, scraping and single-source abuse very well. Distributed floods need mitigation that reasons about the traffic as a whole.
Frequently asked questions
Last updated