limit_req controls how fast requests arrive. limit_conn controls how many are in flight at once. Use limit_req for login and API abuse, limit_conn for downloads and slow clients. Many sites need both, with different keys, and both support a dry run mode so you can test before blocking anyone.
What each one measures
limit_req counts arrivals over time, per key, using a rate such as 5r/s with an optional burst. It answers: is this client sending too many requests?
limit_conn counts concurrent connections per key. The docs say a connection is counted only when it has a request being processed and the whole request header has been read. It answers: is this client holding too many slots open?
Pick by problem
| Problem | Better fit |
|---|---|
| Password guessing on /login | limit_req with a low rate |
| Scraping a catalog | limit_req with a modest burst |
| One client opening many slow downloads | limit_conn per address |
| One virtual server overwhelmed | limit_conn on server name |
| Slow request trickling | timeouts first, then limit_conn |
A combined example
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s;
server {
limit_conn perip 10;
limit_conn perserver 100;
limit_conn_status 429;
location /login {
limit_req zone=login burst=10 nodelay;
limit_req_status 429;
}
}
This follows the pattern in the nginx documentation, which shows 10 connections per client address and 100 per virtual server.
Details that surprise people
- In HTTP/2 and HTTP/3, each concurrent request counts as a separate connection for limit_conn. A browser opening many parallel streams can hit a low limit quickly, so do not copy an HTTP/1 number.
- Several limit_conn directives are inherited only if the current level defines none. The same holds for limit_req.
- Both default to status 503. Choose 429 on purpose.
- If a zone is exhausted, limit_conn returns the error to further requests. Size zones for your peak, remembering one megabyte holds about 16,000 64-byte states.
- Behind a proxy or balancer, the key is the balancer's address unless you set up real client address handling. See the real_ip post. A per address limit on the wrong address blocks everyone at once.
Test with dry run
Both modules have a dry run directive, off by default. In this mode traffic is not limited, but excess is still counted, and a status variable ($limit_conn_status or $limit_req_status) reports what would have happened. Add it to your log format and run for a day.
Limits of these tools
They key on an address. Attackers with many addresses and users sharing one NAT both break that model. Pair them with account level controls and monitoring, as listed in the API resource limits checklist.
Try it yourself
Open the demo and click through the API scan. Look for findings about missing limits on endpoints.
Click through it at your own pace, or open it full screen.
Quick check
Which directive stops one client from holding many concurrent downloads?
limit_conn, keyed on the client address.
Sources
Last reviewed: 19 October 2026.