Security

Rate limiting the endpoints that actually get abused

Blanket limits annoy real users and barely inconvenience attackers. The endpoints worth protecting are a short, specific list.

3 June 2026 2 min read Mohammad Aaquib Jawed

A global request limit is the usual first attempt and it is close to useless: set high enough not to break real usage, it is far above what an attacker needs to brute-force a password.

Useful limiting is per-endpoint, because the endpoints that get abused are specific and few.

The short list

Login, by both IP and account. Per-IP alone is defeated by a distributed attempt; per-account alone lets one IP work through many accounts. You want both, and the per-account limit is what stops credential stuffing.

Password reset, because it sends email — an unlimited endpoint that emails an arbitrary address is a spam relay with extra steps.

Signup, or you will discover how many accounts a script can create overnight.

Anything expensive: search, export, report generation, and any endpoint calling a paid third-party API. Here the risk is cost and capacity rather than compromise.

Anything enumerable — an endpoint whose response differs for existing and non-existing records lets someone map your users, given enough requests.

Throttle, block, allow

Throttling slows a client that exceeds a rate. It suits legitimate users who occasionally go too fast.

Blocking rejects outright and suits behaviour with no legitimate explanation — requests for .env, obvious injection strings in a parameter, known-bad agents.

Allow-listing exempts your own monitoring and internal traffic, and forgetting it is how you page yourself at three in the morning.

Limit by what the endpoint costs and what it reveals, not by a single number applied to everything.

Getting the identity right

Limiting by IP is the default and it is coarse. An office or a mobile network shares one address, so a per-IP limit tuned for an individual will affect a group.

Where a user is authenticated, limit by account. For anonymous endpoints, IP plus something else — a fingerprint, the target account for login attempts — is more precise than either alone.

And behind a proxy, the remote address is the proxy. You must read the forwarded header *and* trust only your own proxy to set it, or an attacker simply supplies a fresh one per request and your limiter counts nothing.

What the response should say

Return 429 with a Retry-After header. Well-behaved clients back off, which is the outcome you want.

Do not explain the limits in detail. "Too many requests" is sufficient; a message revealing the exact window and threshold is a specification for staying just underneath it.

All writing Reply by email