Uptime monitoring from outside your own infrastructure that alerts you the minute your site goes down — upordown.online
Rate limiting looks like a ten-minute job. Pick a number, put it in front of the API, done. Then on Monday morning a customer with 300 employees behind one office NAT gets blocked as a single "abusive client", your own mobile app retries every 429 immediately and makes things worse, and your uptime monitor starts reporting an outage that's really your own limiter.
None of that is a reason not to rate limit. It's a reason to treat the limiter like any other piece of production infrastructure. Here's the checklist I'd go through before turning one on.
Before you pick a number
1. Measure current traffic per client first
Don't guess the limit. Pull a week of access logs and compute requests per minute per key (API key, user ID or IP) at the 99th percentile and the maximum. A limit below your heaviest legitimate client's normal peak is an outage you've scheduled for yourself.
# requests per minute per IP, busiest first (combined log format) awk '{print $1, substr($4,2,17)}' access.log | sort | uniq -c | sort -rn | head 2. Decide what you're limiting by
IP address is the easiest key and the worst one for business customers. Offices, universities, mobile carriers using carrier-grade NAT and corporate VPNs put thousands of real people behind a handful of addresses. Where you can, limit authenticated traffic by API key or user ID and keep IP-based limits for unauthenticated endpoints such as login and signup.
3. Make sure you see the real client IP
If you're behind a load balancer or CDN, every request may appear to come from the same few proxy addresses. Rate limiting on those blocks everyone at once. Configure your server to trust X-Forwarded-For (or the CDN's own header) only from the proxy's known address ranges, and verify the result in your logs before going live.
4. Pick limits per endpoint, not one global number
A search endpoint, a login endpoint and a static config endpoint have nothing in common. Login wants a tight limit (credential stuffing). Search might need a moderate one (expensive queries). A cached read endpoint barely needs one. One global number is either too loose for login or too tight for everything else.
How the limiter behaves
5. Return 429 with a Retry-After header
A 429 Too Many Requests with Retry-After: 30 tells well-behaved clients exactly what to do. Adding the remaining-quota headers (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, or the older X-RateLimit-* names) lets clients slow down before they hit the wall. Don't return 403 or 503 for rate limits: clients and monitors treat those as different problems.
6. Allow bursts
Real clients are bursty. A page load can fire 15 API calls in 200 milliseconds and then nothing for a minute. A token bucket or a sliding window with a burst allowance handles that; a strict "max 10 per second" fixed window punishes normal page loads. In Nginx that's the difference between limit_req zone=api; and limit_req zone=api burst=20 nodelay;.
7. Decide what happens when the limiter's store is down
If your limiter counts requests in Redis and Redis becomes unreachable, does every request fail, or does the limiter let traffic through? For most APIs, failing open (allow traffic, log loudly) is better than turning a cache outage into a full API outage. Whatever you choose, choose it on purpose and test it.
8. Exempt your own infrastructure deliberately
Health checks from your load balancer, your uptime monitor, internal services and webhooks from partners shouldn't compete with customer traffic for the same quota. Exempt them explicitly, by authenticated identity rather than by "anything from this IP range forever", and write the exemptions down where the next person will find them.
Want to know if the limiter is blocking real users? upordown.online checks the same paths your customers hit, so a 429 on a critical endpoint shows up as an alert, not a support ticket.
Rolling it out
9. Start in log-only mode
Run the limiter for a few days in a mode that records what it would have blocked without blocking anything. Most limiters support this (limit_req_dry_run on; in Nginx 1.17.1+, "count" or "log" actions in most WAFs). Look at who would have been limited. You'll find at least one legitimate integration you didn't know about.
10. Check your own clients' retry logic
Your web app, mobile app and SDKs need to respect Retry-After and back off with jitter. A client that retries a 429 immediately, in a loop, turns a brief limit into sustained overload. Fix the clients before the limiter ships, especially mobile apps, since old versions stay installed for months.
11. Monitor 429s as their own metric
Track the 429 rate per endpoint and per client, and alert on sudden changes. A spike in 429s means either an attack (the limiter working) or a bad limit (the limiter causing an outage). You want to know which within minutes, not when support tickets arrive. Make sure your uptime checks treat 429 distinctly too: a check that gets throttled should tell you "throttled", not "down".
12. Document the limits publicly and give people a way to ask for more
Publish the limits in your API docs, include the headers in the examples, and give heavy users a way to request a higher quota. A customer who knows the limit designs around it. A customer who discovers it in production files an angry ticket.
The one-sentence version
Measure first, key on identity rather than IP, return honest 429s with Retry-After, roll out in log-only mode, and watch your 429 rate like any other error rate.
If you're watching your API from the outside, a monitor such as upordown.online will show you quickly whether a limit change made the API slower or started turning legitimate checks away.
Related reading
- How to fix 502 Bad Gateway in Nginx: find the error log line that tells you why
- Rotating an API key or database password? A 12-point checklist so the rotation isn't the outage
- Putting a CDN in front of your site? A 12-point checklist so the CDN isn't your next outage
- Your health check returns 200 while users get errors: 7 health check mistakes
What's the worst thing a rate limiter has blocked for you? Mine was a customer's entire head office, on a Monday, behind one IP address.