Category: Web Server and InfrastructureUpdated 2026-10-18

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:

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:

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