# Nginx Rate Limiting Not Working
Rate limiting should protect your API from abuse, but limits aren't enforced. Clients exceed the allowed requests without consequence, or legitimate users get blocked while abusers continue. The rate limit configuration exists, but requests flow through unrestricted.
Understanding Nginx Rate Limiting
- 1.Nginx rate limiting uses:
- 2.
limit_req_zone- defines the zone and key - 3.
limit_req- applies the limit to a location - 4.Key (usually
$binary_remote_addr) - identifies clients
Check if rate limiting is active:
``bash
curl -I http://example.com/api | grep -i "retry"
On limit exceeded, Nginx returns 503 or 429 with headers indicating retry-after.
Common Cause 1: Limit Zone Not Defined
The zone must be defined in the http block before use.
Problematic config:
``nginx
server {
location /api {
limit_req zone=api_limit burst=10;
# api_limit zone not defined
}
}
Solution: Define zone in http block: ```nginx http { limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server { location /api { limit_req zone=api_limit burst=10 nodelay; } } } ```
Zone parameters explained:
- $binary_remote_addr - key (client IP, uses less memory than $remote_addr)
- zone=api_limit:10m - zone name and size (10MB stores ~160k IPs)
- rate=10r/s - 10 requests per second per IP
Common Cause 2: Wrong Key for Identification
Limiting by IP doesn't work when users share IPs (NAT, proxies).
Problem: All office users share one IP:
``nginx
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# One company IP blocked after 10 requests total, not per user
Solution: Use more specific key: ```nginx # Limit by API key header limit_req_zone $http_x_api_key zone=api_limit:10m rate=10r/s;
# Limit by authenticated user limit_req_zone $remote_user zone=user_limit:10m rate=10r/s;
# Limit by session cookie map $cookie_session $limit_key { "" $binary_remote_addr; # No cookie, use IP default $cookie_session; # Has cookie, use session }
limit_req_zone $limit_key zone=api_limit:10m rate=10r/s; ```
Common Cause 3: Burst Handling Misconfigured
Burst allows temporary spikes but the behavior depends on nodelay.
Without nodelay:
``nginx
limit_req zone=api_limit burst=10;
# Requests above rate are queued, released at rate
# Client waits for queued requests
With nodelay:
``nginx
limit_req zone=api_limit burst=10 nodelay;
# Burst requests handled immediately
# Only burst limit enforced, not rate within burst
**Problem: nodelay makes burst too generous:**
``nginx
limit_req zone=api_limit burst=50 nodelay;
# Client can send 60 requests instantly (1 + burst)
# Then blocked
Solution: Tune burst and nodelay: ```nginx # Strict limiting - no burst limit_req zone=api_limit;
# Moderate - burst with delay limit_req zone=api_limit burst=5;
# Aggressive burst for API limit_req zone=api_limit burst=20 nodelay; ```
Common Cause 4: Limit Not Applied to Location
Zone defined but not applied to the specific location.
Problematic config: ```nginx http { limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server { location /api { # limit_req missing here }
location /api/data { limit_req zone=api_limit; } } }
# Requests to /api not limited, only /api/data limited ```
Solution: Apply to all protected locations: ```nginx http { limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server { # Apply to all API routes location /api { limit_req zone=api_limit burst=10; }
location /api/data { limit_req zone=api_limit burst=10; } } } ```
Or use nested locations: ```nginx location /api { limit_req zone=api_limit burst=10;
location /api/special { # Different limit for special endpoint limit_req zone=special_limit burst=50 nodelay; } } ```
Common Cause 5: Whitelist Not Working
Whitelist should exempt certain IPs but they're still limited.
Problematic config: ```nginx geo $limit { default 1; 10.0.0.0/8 0; # Internal IPs exempt }
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api { limit_req zone=api_limit; # Still using IP as key, geo variable unused } ```
Solution: Use geo variable in zone definition: ```nginx geo $limit { default 1; 10.0.0.0/8 0; 192.168.0.0/16 0; }
map $limit $limit_key { 0 ""; # No limit 1 $binary_remote_addr; # Limit by IP }
limit_req_zone $limit_key zone=api_limit:10m rate=10r/s;
location /api { limit_req zone=api_limit burst=10 nodelay; } ```
Empty key means no limiting.
Common Cause 6: Multiple Limits Conflict
Multiple rate limits on same location can cause confusion.
Problematic config:
``nginx
location /api {
limit_req zone=api_limit burst=10;
limit_req zone=slow_limit burst=5; # Second limit
# Both limits apply, stricter one wins
}
Solution: Use different zones for different purposes: ```nginx # IP-based limit limit_req_zone $binary_remote_addr zone=ip_limit:10m rate=10r/s;
# Global limit for all clients limit_req_zone $server_name zone=global_limit:10m rate=1000r/s;
location /api { limit_req zone=ip_limit burst=10; limit_req zone=global_limit burst=100; # Global cap } ```
Common Cause 7: Status Code Not Customized
Default 503 Service Unavailable is confusing for rate limits.
Solution: Return 429 Too Many Requests: ```nginx limit_req_status 429;
location /api { limit_req zone=api_limit burst=10; error_page 429 = @rate_limit_exceeded; }
location @rate_limit_exceeded { default_type application/json; return 429 '{"error": "Rate limit exceeded", "retry_after": 60}'; } ```
Common Cause 8: Rate Limit Logging Missing
Can't see who's being limited.
Solution: Add rate limit logging: ```nginx http { limit_req_log_level warn; # Log level for limit events
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; } ```
Log format to include limit info:
``nginx
log_format limit '$remote_addr - $status [$time_local] '
'limit_req_status=$limit_req_status';
Note: $limit_req_status is only available in Nginx Plus. For open source, check error logs for rejected requests.
Common Cause 9: Delay Parameter Not Understood
delay parameter controls when requests within burst are delayed.
Problem: All burst requests delayed:
``nginx
limit_req zone=api_limit burst=10;
# First 10 requests above rate all delayed
Solution: Use delay to allow partial instant burst:
``nginx
limit_req zone=api_limit burst=10 delay=5;
# First 5 over-rate requests instant
# Remaining burst requests delayed
Verification Steps
- 1.Test rate limit enforcement:
- 2.```bash
- 3.# Send requests rapidly
- 4.for i in {1..20}; do
- 5.curl -s -o /dev/null -w "%{http_code}\n" http://example.com/api/data
- 6.done
# Should see 200s then 429/503 ```
- 1.Monitor error logs:
- 2.```bash
- 3.tail -f /var/log/nginx/error.log | grep "limiting"
- 4.
` - 5.Check limit status in response:
- 6.```bash
- 7.curl -v http://example.com/api/data 2>&1 | grep -E "HTTP|X-RateLimit"
- 8.
` - 9.Test burst behavior:
- 10.```bash
- 11.# Burst test - 20 requests in 1 second
- 12.time for i in {1..20}; do curl -s http://example.com/api/data & done; wait
- 13.
`
Complete Working Configuration
```nginx http { # Whitelist internal IPs geo $limit { default 1; 10.0.0.0/8 0; 192.168.0.0/16 0; 127.0.0.1 0; }
map $limit $limit_key { 0 ""; 1 $binary_remote_addr; }
# API rate limit limit_req_zone $limit_key zone=api_limit:10m rate=10r/s;
# Strict login limit limit_req_zone $binary_remote_addr zone=login_limit:5m rate=5r/m;
limit_req_status 429; limit_req_log_level warn;
server { location /api { limit_req zone=api_limit burst=20 delay=5;
add_header X-RateLimit-Limit 10 always; add_header X-RateLimit-Remaining $limit_req_remaining always;
proxy_pass http://backend; }
location /login { limit_req zone=login_limit nodelay;
error_page 429 = @rate_limited; }
location @rate_limited { default_type application/json; return 429 '{"error": "Too many requests"}'; } } } ```
Quick Reference
| Symptom | Cause | Fix |
|---|---|---|
| No limiting happens | Zone not applied | Add limit_req zone=name |
| Legitimate users blocked | NAT/shared IPs | Use API key or session as key |
| Spikes still blocked | No burst configured | Add burst=N |
| Too many requests allowed | nodelay + large burst | Reduce burst or remove nodelay |
| Internal IPs limited | Whitelist not applied | Use geo/map for whitelist |
| Wrong error code | Default 503 | Set limit_req_status 429 |
| Can't see who limited | No logging | Set limit_req_log_level |
Rate limiting requires understanding the key, zone, burst, and enforcement relationship. The key determines who's limited, the zone stores state, burst handles spikes, and nodelay/delay control timing.