Rate limiting fails in two directions. Set it too loose and it stops nothing: credential stuffing runs at one attempt per second per IP, comfortably under any casual threshold. Set it too tight and you throttle a university, a mobile carrier or your own mobile app, because thousands of real users share one address. Getting it right is a design sequence — choose what you count, derive budgets from measured traffic, and pick penalties that de-escalate — not a single number typed into a dashboard.
What rate limiting is actually for
Be precise about the threat, because the configuration follows from it. Volumetric floods are the wrong target — your CDN’s DDoS layer absorbs those before rule logic runs. Rate limits earn their keep against low-and-slow abuse: credential stuffing on login, card testing on payment endpoints, scraping of catalogue pages, and runaway clients — including your own buggy retry loops — hammering an API. Each has a different natural signature, which is why one global limit protects nothing well. Write down the two or three abuse cases you actually see in your logs and design a rule per case.
Choose the counting key
The counting key — what the counter increments per — matters more than the threshold. Per-IP is the default and the weakest: carrier-grade NAT puts entire mobile networks behind a handful of addresses, so a per-IP limit tight enough to stop an attacker will hit real users first, while attackers rotate through residential proxy pools that never reuse an address. Better keys exist for authenticated traffic: the API token, the session cookie, the user ID — these follow the actor, not the network. For unauthenticated endpoints, combine dimensions where your platform allows it (IP plus path, IP plus client fingerprint) and keep pure per-IP budgets loose enough to be a backstop rather than the primary control. Count what reaches the origin, not what hits the edge, if your goal is origin protection — cached hits cost you little and inflate the counter unfairly.
Set budgets from measured traffic
Never guess a threshold. Pull a week of logs per protected endpoint and look at the distribution of requests per key per window: the p99 legitimate client tells you the floor, and known-abusive sessions tell you where abuse begins. The gap between them is your budget zone — on login endpoints it is usually wide (real users attempt a handful of logins per minute; stuffers need hundreds), on search and catalogue endpoints it is narrower and a limit alone may need help from bot rules. Set the initial threshold generously inside the gap, deploy the rule in log-only mode, and watch a full week of who would have been limited before any request is actually blocked. The method mirrors the WAF rollout: observation before enforcement, every time.
Penalties: block, delay, challenge
The penalty is a dial, not a verdict. A hard block with HTTP 429 — plus an honest Retry-After header — is right for machine traffic and API clients, which are built to back off. For browser traffic, a challenge is kinder: a JavaScript or managed challenge lets a shared-IP false positive pass in seconds while stopping the script. Some platforms also offer a timed penalty (exceed the budget, wait out a cooldown) which de-escalates automatically without support tickets. Whatever you pick, make the limited response cheap and cacheable at the edge — a rate-limited request that still reaches your origin has only moved the problem. And log every limited request with its key: the penalty log is where you discover the mobile-carrier false positive before your app-store reviews do.
Endpoint tiers and the rollout
Finish by writing the tier table: authentication and payment endpoints get the tightest keys and budgets, write APIs come next, search and expensive dynamic pages next, and static cached content usually needs no limit at all. Keep the table in version control beside the WAF exception register — the two lists drift for the same reasons. Roll out tier by tier, log-first, exactly as in section three, and revisit budgets after every product launch, because a new app release changes legitimate request patterns overnight. Rate limits pair naturally with bot rules — the limit caps the pace, the bot layer judges the client — and the taxonomy of what you are defending against is mapped in our DDoS attack taxonomy. For API-specific budgets, see putting a CDN in front of your API.
