limit_req
limit_req is the nginx directive that turns on request rate limiting for a server or location. It points to a shared memory zone declared with limit_req_zone, which tracks a key such as the client IP address, and enforces a maximum rate with an optional burst. Requests over the limit are delayed or rejected with an error status.
Syntax: limit_req_zone and limit_req work as a pair
The ngx_http_limit_req_module limits the request processing rate per key using the "leaky bucket" method. It needs two directives:
limit_req_zone $key zone=name:size rate=rate;
limit_req zone=name [burst=number] [nodelay | delay=number];
limit_req_zone is valid only in the http context. The key can be text, variables or a combination, and requests with an empty key value are not accounted. The rate is written in r/s, or in r/m below one request per second, so 30r/m is half a request per second. limit_req is valid in http, server and location and has no default.
Three companions have documented defaults: limit_req_status is 503, limit_req_log_level is error (delays are logged one level lower), and limit_req_dry_run is off. A state with $binary_remote_addr takes 128 bytes on 64-bit platforms, so a 1 MB zone holds about 8 thousand of them. When the zone is full, the least recently used state is removed.
Burst, nodelay and delay in plain terms
With the default burst of zero, a request that arrives faster than the zone rate is refused. The three parameters of limit_req change what happens to such excessive requests:
burst=Nallows up to N excessive requests. They are delayed so that processing stays at the defined rate, and once their number exceeds N the request is terminated with an error.nodelaystops excessive requests from being delayed while they are limited. Requests inside the burst are served at once, and the rest are refused.delay=N(nginx 1.15.7 and later) sets the point at which excessive requests start to be delayed. The default is zero, so all excessive requests are delayed.
For example, rate=1r/s with burst=5 means on average no more than one request per second, with bursts of up to 5. nginx tracks time in milliseconds, so 10r/s spaces requests about 100 ms apart. Choose nodelay for interactive pages where a queued response feels like a hang, and plain burst where smoothing out traffic to a backend matters more than latency.
A minimal example and how to test it
This protects a login endpoint at about one attempt every two seconds per client address, with room for a short burst, and answers with the status code from RFC 6585 defined for this case:
http {
limit_req_zone $binary_remote_addr zone=login:10m rate=30r/m;
server {
location = /login {
limit_req zone=login burst=5 nodelay;
limit_req_status 429;
}
}
}
Check the syntax and reload without dropping connections:
sudo nginx -t && sudo systemctl reload nginx
for i in $(seq 1 12); do curl -s -o /dev/null -w "%{http_code}
" https://example.com/login; done
Run the loop from a machine you control. The first few requests return the normal code and the rest return 429. To tune values on live traffic first, add limit_req_dry_run on; (nginx 1.17.1 and later): excess requests are counted but not limited, and the $limit_req_status variable records PASSED, DELAYED or REJECTED, with _DRY_RUN variants.
Gotchas, and where it fits in hardening
Most limit_req surprises come from the key and from inheritance:
- Behind a CDN or reverse proxy,
$binary_remote_addris the proxy's address, so everyone shares one budget. Restore the real client address first, for example with the realip module. - Limits are inherited from the previous level only if the current level has no
limit_reqof its own. Several directives can apply to one request, for example per client and per server. - A single page load can fire many requests, so apply tight limits to login or API paths, not to the whole site.
- The default 503 looks like an outage in monitoring. Set
limit_req_status 429for clarity.
limit_req slows abuse, it does not identify it. Pair it with fail2ban for repeat offenders, and see the concept page on rate limiting. The nginx security guide and the DDoS protection guide show where it sits among other controls.
Related terms: Rate limiting, nginx, fail2ban, Brute-force attack, WAF.
Frequently asked questions
What does limit_req do in nginx?
It applies a rate limit from a zone defined with limit_req_zone to a server or location. Requests faster than the configured rate are delayed within the burst and rejected beyond it.
What is the difference between burst and nodelay?
burst sets how many excessive requests are tolerated, and by default they are delayed to match the rate. Adding nodelay serves requests inside the burst immediately and refuses the rest.
Why does limit_req return 503 instead of 429?
nginx rejects requests with 503 unless limit_req_status says otherwise. Add limit_req_status 429; to return Too Many Requests, which RFC 6585 defines for this purpose.
Where do I put limit_req_zone?
Only in the http context, outside server blocks. You then reference the zone by name with limit_req inside http, server or location.
Why are all my users limited together?
Usually the key is the same for everyone, because nginx sits behind a proxy or CDN and sees one address. Restore the real client IP before using $binary_remote_addr as the key.
Want to see where your own server stands?
Run the free, read-only server check, or open the Security Lab and watch the detect, contain, recover, verify loop in action.
Get your free server checkOpen the Security Lab