In Nginx, the limit_req_zone and limit_req directives work together to implement request rate limiting. The basic syntax looks like this:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location / {
limit_req zone=mylimit burst=20 nodelay;
}
}
The burst parameter defines how many requests can exceed the base rate before Nginx starts rejecting requests. Without nodelay, excess requests are queued and processed at the defined rate.
For example, with rate=10r/s and burst=5:
- First 10 requests in a second are processed immediately
- Next 5 requests are queued and processed in subsequent 500ms
- Any additional requests get 503 response
The nodelay parameter changes this behavior fundamentally. When present:
- It allows the
burstnumber of requests to be processed immediately - No queuing occurs - either the request is processed now or rejected
- The burst "bucket" is still refilled at the specified rate
Consider these three configurations with rate=10r/s:
# Case 1: Basic rate limiting
limit_req zone=mylimit;
# Case 2: With burst but no nodelay
limit_req zone=mylimit burst=5;
# Case 3: With burst and nodelay
limit_req zone=mylimit burst=5 nodelay;
Behavior comparison:
| Requests in 100ms | Case 1 | Case 2 | Case 3 |
|---|---|---|---|
| 10 | All processed | All processed | All processed |
| 15 | 10 processed, 5 rejected | 10 processed, 5 delayed | 15 processed |
| 20 | 10 processed, 10 rejected | 10 processed, 5 delayed, 5 rejected | 15 processed, 5 rejected |
When to use nodelay:
- When you need to allow occasional traffic spikes
- When request queuing isn't practical (e.g., API endpoints)
- When you want to strictly enforce rate limits after the burst
When to avoid nodelay:
- When you want smooth rate limiting without sharp cutoffs
- For endpoints where delayed responses are acceptable
Here's a more sophisticated setup that combines multiple rate limits:
geo $limited {
default 0;
10.0.0.0/8 1;
}
map $limited $limit_key {
0 $binary_remote_addr;
1 "";
}
limit_req_zone $limit_key zone=strict:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=normal:10m rate=10r/s;
server {
location /api/ {
limit_req zone=strict burst=3 nodelay;
limit_req zone=normal burst=10;
}
}
When configuring Nginx's limit_req directive, the combination of burst and nodelay often causes confusion. Let's break down their actual behavior:
limit_req zone=one burst=5 nodelay;
Without nodelay, burst creates a queue for excess requests:
limit_req zone=one burst=5;
# 6th request gets delayed (503 if queue fills completely)
Adding nodelay changes the behavior fundamentally:
- Burst size still defines the maximum number of excess requests
- Instead of delaying, Nginx immediately processes burst requests
- However, it still counts them against the rate limit
This combination is perfect for handling temporary traffic spikes while maintaining overall rate control:
# Allow 5 immediate excess requests, then enforce strict limit
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req zone=api burst=5 nodelay;
To observe the difference, try these test cases:
# Case 1: Without nodelay
ab -n 20 -c 5 http://yoursite.com/
# Case 2: With nodelay
ab -n 20 -c 5 http://yoursite.com/limited-endpoint
- Myth:
nodelaymakesburstirrelevant - Truth: Burst still controls how many excess requests are allowed before enforcement
- Myth: Nodelay disables rate limiting
- Truth: It just changes the enforcement method
Combine with logging for debugging:
limit_req_status 429;
limit_req_log_level warn;