NGINX Rate Limiting: Protect Your Server with limit_req_zone in Minutes
Stop abusive traffic with NGINX's built‑in rate limiting. This guide explains limit_req_zone, shows a practical config, tests it, and discusses trade‑offs.
11 Sept 2026, 04:09 UTC

Problem Definition
When a website receives a sudden surge of requests—whether from legitimate users, scrapers, or a DDoS attack—each incoming connection consumes CPU, memory, and network bandwidth. If the server is overwhelmed, downstream services become slow or unresponsive, and the user experience degrades. Application‑level throttling (e.g., in PHP or Node) is too late: by the time the code runs, resources may already be exhausted.
NGINX offers a lightweight, server‑side throttle that can reject or delay requests before they hit the application layer. The built‑in limit_req_zone and limit_req directives let you set a per‑client or per‑route request rate, using shared memory for state and without external tooling.
Configuration Basics
1. Declaring a Shared Memory Zone
The limit_req_zone directive creates a zone in shared memory that stores the state for each key (typically an IP address). The syntax is:
limit_req_zone $key zone=name:size rate=rate;
$key– expression that identifies the client. Commonly$binary_remote_addr(binary IP) or$remote_addr(text).zone=name:size– gives the zone a name and the amount of memory to allocate. A good rule of thumb is10kper 1 000 unique IPs, e.g.,zone=addr:10mfor ~10 000 IPs.rate=rate– requests per second. Use10r/sfor ten requests per second.
2. Applying the Limiter to a Context
The limit_req directive references the zone and optionally allows a burst of requests that exceed the rate. Syntax:
limit_req zone=name [burst=number] [nodelay];
burst– how many requests can be queued before the limiter starts rejecting.nodelay– if set, requests over the burst are rejected immediately (no delay). Without it, NGINX will delay them until the bucket refills.
Worked Example
Below is a minimal configuration that limits each client IP to 10 requests per second, allows a burst of 5, and logs 429 responses. Place the snippet in your http block or a site‑specific server block.
# Define a shared memory zone for IPs
limit_req_zone $binary_remote_addr zone=addr:10m rate=10r/s;
server {
listen 80;
server_name example.com;
# Apply the limiter to all requests in this server
limit_req zone=addr burst=5 nodelay;
location / {
proxy_pass http://upstream_backend;
}
}
Testing the limiter:
- Validate syntax:
sudo nginx -t - Reload NGINX:
sudo systemctl reload nginx - Generate traffic with
ab(ApacheBench) orwrk:ab -n 1000 -c 50 http://localhost/ - Inspect the access log for 429 status codes. Example log snippet:
192.168.1.10 - - [24/Sep/2026:04:32:10 +0000] "GET / HTTP/1.1" 429 0 "-" "curl/7.68.0"
With the above configuration, after the first 10 requests per second, any additional request from the same IP will receive a 429 Too Many Requests response immediately.
Trade‑offs & Limitations
| Aspect | Benefit | Limitation |
|---|---|---|
| Memory usage | Low—only a few bytes per tracked key. | Zone size must match expected unique IP count; otherwise, entries may be evicted. |
| Granularity | Per‑request rate control. | Cannot limit by bandwidth or WebSocket traffic. |
| False positives | Protects server resources. | Legitimate users behind a NAT may share an IP and hit the limit. |
| Proxy scenarios | Works with real client IP if preserved. | Must use $http_x_forwarded_for or similar if behind a load balancer. |
| Burst handling | Allows temporary spikes. | High burst values can let large bursts through, potentially overloading upstream. |
Practical Next Steps
- Prototype the limiter in a staging environment before deploying to production.
- Monitor
/var/log/nginx/access.logfor429entries to confirm the limiter is active. - Adjust
rateandburstbased on observed traffic patterns; uselimit_req_status=503if you prefer a 503 response. - When behind a reverse proxy, add a
real_ip_header X-Forwarded-For;directive and use$http_x_forwarded_foras the key. - Consider combining
limit_req_zonewithlimit_conn_zoneif you also need to cap concurrent connections.
Rate limiting is a first‑line defense that keeps your application responsive while you investigate deeper issues. By configuring limit_req_zone and limit_req correctly, you can protect resources, reduce abuse, and maintain a better user experience—all without adding external services.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.