Operations 14 min read

Master Nginx Rate Limiting: Prevent DDoS and Control Traffic with Leaky Bucket

This guide explains Nginx's rate‑limiting feature, covering its leaky‑bucket algorithm, basic and advanced configurations—including limit_req_zone, limit_req, burst, nodelay, whitelisting, logging, and custom error codes—to help you protect upstream servers and mitigate abuse.

Full-Stack DevOps & Kubernetes
Full-Stack DevOps & Kubernetes
Full-Stack DevOps & Kubernetes
Master Nginx Rate Limiting: Prevent DDoS and Control Traffic with Leaky Bucket

Rate limiting (rate‑limiting) is a practical Nginx feature often misunderstood or misconfigured; it restricts the number of HTTP requests a client can make within a given time window, whether for a simple homepage GET or a login POST.

Nginx Rate Limiting Mechanism

Nginx uses the leaky‑bucket algorithm, common in networking, to handle burst traffic when bandwidth is limited. Think of a bucket that leaks water at a constant rate; incoming requests fill the bucket, excess water overflows and is dropped, mirroring how Nginx queues and discards requests.

Basic Configuration

The two main directives are limit_req_zone and limit_req:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;</code>
<code>server {</code>
<code>    location /login/ {</code>
<code>        limit_req zone=mylimit;</code>
<code>        proxy_pass http://my_upstream;</code>
<code>    }</code>
<code>}
limit_req_zone

defines the shared memory zone and parameters; limit_req enables the limit in a specific context (e.g., the /login/ location). The three parameters are:

Key : the variable identifying the client (e.g., $binary_remote_addr).

Zone : shared memory area storing per‑IP state; the example allocates 10 MiB, enough for ~160 000 IPs.

Rate : maximum request rate, here 10 requests per second (equivalent to 1 request per 100 ms).

When the shared memory is full, Nginx removes old entries; if still insufficient, it returns 503 (Service Temporarily Unavailable). It also deletes up to two unused entries per creation to avoid memory exhaustion.

Since limit_req_zone only defines the zone, you must add limit_req to actually enforce the limit.

Handling Bursts

If two requests arrive within 100 ms, the second would be rejected with 503. To allow temporary bursts, use the burst parameter:

location /login/ {</code>
<code>    limit_req zone=mylimit burst=20;</code>
<code>    proxy_pass http://my_upstream;</code>
<code>}
burst

defines how many excess requests can be queued (20 in the example). Queued requests are processed at the configured rate, and only when the queue exceeds the size does Nginx return 503.

Zero‑Delay Queuing

Adding nodelay makes Nginx forward a request immediately if a queue slot is available, without waiting for the next rate‑limited interval:

location /login/ {</code>
<code>    limit_req zone=mylimit burst=20 nodelay;</code>
<code>    proxy_pass http://my_upstream;</code>
<code>}

With nodelay, the first 21 simultaneous requests are forwarded (20 occupy the queue, 1 passes directly); excess requests are rejected.

Advanced Example: Whitelisting

Combine geo and map to exempt certain IP ranges from rate limiting:

geo $limit {</code>
<code>    default         1;</code>
<code>    10.0.0.0/8      0;</code>
<code>    192.168.0.0/24  0;</code>
<code>}</code>
<code>map $limit $limit_key {</code>
<code>    0 "";</code>
<code>    1 $binary_remote_addr;</code>
<code>}</code>
<code>limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;</code>
<code>server {</code>
<code>    location / {</code>
<code>        limit_req zone=req_zone burst=10 nodelay;</code>
<code>        # ...</code>
<code>    }</code>
<code>}

Clients in the whitelist receive an empty key, so no limit is applied; all others are limited to 5 r/s.

Multiple limit_req in One Location

You can stack several limit_req directives; the strictest limit wins. Example extending the whitelist scenario:

http {</code>
<code>    limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;</code>
<code>    limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;</code>
<code>    server {</code>
<code>        location / {</code>
<code>            limit_req zone=req_zone burst=10 nodelay;</code>
<code>            limit_req zone=req_zone_wl burst=20 nodelay;</code>
<code>        }</code>
<code>    }</code>
<code>}

Whitelisted IPs are limited to 15 r/s, others to 5 r/s.

Logging Configuration

By default Nginx logs limited requests at error level. Use limit_req_log_level to change it:

location /login/ {</code>
<code>    limit_req zone=mylimit burst=20 nodelay;</code>
<code>    limit_req_log_level warn;</code>
<code>    proxy_pass http://my_upstream;</code>
<code>}

Log entries include fields such as limiting requests, excess, zone, client IP, server, request line, and host.

Custom Error Status

Instead of the default 503, you can set another status code with limit_req_status (e.g., 444):

location /login/ {</code>
<code>    limit_req zone=mylimit burst=20 nodelay;</code>
<code>    limit_req_status 444;</code>
<code>}

Deny All Requests for a Specific URL

To block a URL entirely, use deny all inside the location:

location /foo.php {</code>
<code>    deny all;</code>
<code>}

Summary

The article covered Nginx and Nginx Plus rate‑limiting capabilities, including per‑location request rates, burst and nodelay parameters, whitelist/blacklist configurations, logging of rejected or delayed requests, custom error codes, and how to deny all traffic to a specific location.

backendRate LimitingLeaky Bucketlimit_req
Full-Stack DevOps & Kubernetes
Written by

Full-Stack DevOps & Kubernetes

Focused on sharing DevOps, Kubernetes, Linux, Docker, Istio, microservices, Spring Cloud, Python, Go, databases, Nginx, Tomcat, cloud computing, and related technologies.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.