Rate limiting protects your API from abuse, accidental client bugs causing request storms, and helps ensure fair usage across all clients. Nginx provides efficient, built-in rate limiting without needing application-level changes.
Basic Rate Limiting Configuration
http {
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
}
}
}
rate=10r/s allows 10 requests per second per client IP; burst=20 allows short bursts above that rate, up to 20 extra queued requests; nodelay processes burst requests immediately rather than artificially delaying them.
Understanding the Zone Definition
zone=api_limit:10m allocates 10MB of shared memory to track client request rates — sufficient for roughly 160,000 tracked IP addresses, more than enough for most applications.
Applying Different Limits to Different Endpoints
http {
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
server {
location /api/login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://127.0.0.1:3000;
}
location /api/ {
limit_req zone=general burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
}
}
}
Sensitive endpoints (login, password reset) warrant much stricter limits than general API browsing, since they're common brute-force targets.
Returning a Proper Status Code When Rate Limited
limit_req_status 429;
429 (Too Many Requests) is the correct HTTP status code, letting well-behaved clients know to back off and retry later rather than treating it as a generic error.
Rate Limiting by API Key Instead of IP (More Precise)
If clients authenticate with an API key, rate limit per-key instead of per-IP, avoiding penalizing legitimate users sharing an IP (e.g. behind a corporate NAT) while still controlling individual API key abuse:
limit_req_zone $http_x_api_key zone=apikey_limit:10m rate=100r/m;
Combining Connection Limiting
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location /api/ {
limit_conn conn_limit 10;
}
}
Limits simultaneous open connections per IP, complementing the request-rate limiting above.
Logging Rate-Limited Requests
limit_req_log_level warn;
Logs when clients hit the rate limit, helping identify whether limits need adjustment or whether genuine abuse is occurring.
Testing Your Rate Limits
for i in {1..30}; do curl -o /dev/null -s -w "%{http_code}\n" https://api.yourdomain.com/endpoint; done
Confirm you start seeing 429 responses once the configured limit is exceeded.
Setting Different Limits for Authenticated vs Anonymous Requests
map $http_authorization $limit_key {
"" $binary_remote_addr;
default "";
}
limit_req_zone $limit_key zone=anon_limit:10m rate=5r/s;
A common pattern: apply stricter limits to unauthenticated requests, more generous limits once a client has authenticated.
Common Errors
Legitimate traffic getting rate limited — the rate/burst values may be too conservative for genuine usage patterns; review actual traffic data before tightening limits.
Rate limiting has no effect — verify the limit_req_zone directive is in the http block (not server), and limit_req is correctly referenced within the relevant location block.
Best Practices
- Apply stricter limits to sensitive endpoints (login, password reset) than general browsing
- Return 429 with appropriate
Retry-Afterheaders for well-behaved client handling - Monitor rate-limit logs to distinguish genuine abuse from limits that are too conservative
Related Articles
- How to Build and Secure a REST API on a VPS
- How to Secure a VPS Against DDoS Attacks
- Nginx Performance Tuning: Worker Processes, Caching & Gzip
