Layered rate limiting in Nginx — from limit_req_zone to Cloudflare and back
May 20, 2026 · 1 min read · by Sudhanshu K.
Rate limiting in Nginx is one of those things that exists in every config and works in maybe half of them. The default limit_req block buried in a sample config doesn't survive a real scrape, and certainly not a real attack. Worse, badly-tuned limits will block the legitimate Google crawler the day before your team realises why ranking dropped.
We layer rate limiting at three points: at the edge (Cloudflare or equivalent), at the perimeter (Nginx), and at the application (per-endpoint, per-user). This is the perimeter layer we ship on every managed Nginx install.
Per-route limits with a burst allowance
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
location = /login {
limit_req zone=login burst=3 nodelay;
limit_conn conn 20;
proxy_pass http://app;
}
location /api/ {
limit_req zone=api burst=40 nodelay;
proxy_pass http://app;
}
}/login is the brute-force target — 5 requests per minute is generous for humans and brutal for credential-stuffing bots. /api is much more permissive but still bounded.
The full write-up covers:
- Edge → perimeter → origin: which attacks each layer catches
limit_req_status 429and properRetry-Afterheaders so legitimate clients back offgeodirectives to whitelist known good crawlers- The
$binary_remote_addrvs$remote_addrmemory consideration (4 vs 24 bytes per IP) - Coordinating Cloudflare rule changes with origin limit changes (don't trip both at once)
- Reading the
limit_reqlog lines to triage real attacks from misconfiguration
Reach out if your origin is currently being scraped and you're not sure how to read the logs.
Full article available
Read the full article