Rate limits protect login endpoints and APIs from abuse and keep one client from starving the others.
Request rate limiting uses the leaky bucket algorithm.
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; (in http) defines a shared zone keyed by client address. $binary_remote_addr is compact: a 10 MB zone tracks around 160,000 addresses. The key can be any variable, such as an API key header.
limit_req zone=api; in a location applies it. Without other parameters, requests arriving faster than the rate (here one per 100 ms) are rejected immediately, even short bursts. Two parameters fix that:
burst=20: queue up to 20 excess requests; they are released at the configured rate, so they are delayed.nodelay: process queued burst requests immediately, while still counting them; requests beyond the burst are rejected. This is usually what APIs want: bursts are allowed, sustained abuse is not.delay=8(instead of nodelay): first 8 excess requests go without delay, the rest are delayed.
Rejected requests get 503 by default; set limit_req_status 429; for the correct "Too Many Requests". limit_req_dry_run on; logs what would be limited without enforcing it, ideal for tuning.
limit_conn_zone and limit_conn cap concurrent connections per key, for example to stop one client from opening hundreds of downloads.
Example
http {
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_req_status 429;
server {
listen 80;
location /api/ {
limit_req zone=api burst=20 nodelay; # bursts OK, abuse rejected
proxy_pass http://127.0.0.1:8000;
}
location = /login {
limit_req zone=login burst=3; # excess queued, then 429
proxy_pass http://127.0.0.1:8000;
}
location /downloads/ {
limit_conn perip 2; # 2 concurrent downloads
}
}
}Remember
- limit_req_zone defines key, size and rate; limit_req applies it
- burst queues excess; nodelay serves the burst at once
- Use limit_req_status 429 and dry-run mode while tuning
Watch out for
- Rate limiting by $remote_addr behind a load balancer: every client shares the balancer's IP; use the real_ip module first.