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
burst
number 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:
nodelay
makesburst
irrelevant - 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;