Nginx Limit Request: A Shield Against ‘Guerrilla’ DDoS and Effective Traffic Throttling

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Real-world Experience: When the Server Suddenly Starts “Suffocating”

After 6 months of “battle-tested” experience managing over 10 production server clusters, I realized a fatal flaw: most small and medium websites leave their HTTP gateways completely unprotected. Many developers focus intensely on optimizing code but forget that a simple 5-line Python script running in a loop is enough to freeze Nginx.

I once handled a “nightmare” case: an e-commerce site being spammed by a competitor with 50-100 requests/second on the search bar. This traffic wasn’t enough to saturate the bandwidth (Volumetric DDoS), but it caused MySQL CPU usage to spike to 100%. Consequently, real customers couldn’t complete their checkouts. This is exactly where the Limit Request Module becomes a lifesaver.

Comparing Common Defense Layers

Before diving into configuration, let’s review the protection layers so you know where you stand:

  • Cloudflare (WAF/CDN): An extremely powerful outermost shield that filters junk traffic before it reaches your server. However, if you leak your Origin IP, this shield becomes useless.
  • Firewall (iptables/nftables): Blocks at the Network layer (Layer 3/4). It processes extremely fast but is completely “blind” to content. A firewall cannot distinguish between a real user and a bot scraping data.
  • Nginx Limit Request (Layer 7): The final checkpoint at the application layer. It understands every URL, IP, and Session. This is the perfect tool for handling small-scale attacks or preventing excessive web scraping.

Why is the Leaky Bucket Algorithm Effective?

Nginx uses the Leaky Bucket algorithm to regulate traffic. Imagine requests as water pouring into a bucket, and Nginx processes them through a small hole at the bottom at a fixed rate. If water is poured in too fast and overflows the bucket, excess requests are rejected immediately. This mechanism helps the server maintain a steady pace, avoiding “thermal shock” during traffic spikes.

Detailed Implementation Guide

All configurations will take place in the nginx.conf file or site configuration files within sites-available/.

Step 1: Setting up a Shared Memory Zone

Nginx needs a place to remember which IP has sent how many requests. Add the following line to the http block:

http {
    # 10MB can store approximately 160,000 IP states
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
}

Parameter Breakdown:

  • $binary_remote_addr: Stores the IP in binary format to save memory (only takes 64 bytes per state on a 64-bit system).
  • zone=mylimit:10m: Creates a memory zone named “mylimit” with a 10MB capacity.
  • rate=10r/s: A maximum threshold of 10 requests per second for each IP.

Step 2: Applying to Sensitive Areas

Don’t apply this blindly to the entire site. Focus on resource-intensive areas like search or login pages.

server {
    location /search/ {
        # Allow a "burst" of up to 20 additional requests to be processed immediately
        limit_req zone=mylimit burst=20 nodelay;
        proxy_pass http://backend_app;
    }
}

Step 3: Deep Dive into Burst and Nodelay

This is the most confusing part that often leads to accidentally blocking legitimate users:

  • Burst=20: Like a queue at a ticket counter. If customers arrive too quickly, they can wait in line (up to 20 people) instead of being turned away immediately.
  • nodelay: Without this keyword, Nginx will force customers to wait at the exact rate of 10r/s (very slow). With nodelay, the 20 requests in the queue are processed immediately, but the 21st request onwards will return an error instantly.

Optimizing Experience with Error Code 429

By default, Nginx returns a 503 (Service Unavailable) error. However, to be friendlier to “clean” bots like Googlebot, you should use code 429 (Too Many Requests).

limit_req_status 429;
limit_req_log_level warn;

Logging at the warn level makes it easy to track suspicious IPs via the error.log file without filling up your disk space too quickly.

Testing Load Capacity

Don’t wait until an attack happens to find out your configuration is wrong. You can use the ab (Apache Benchmark) tool to simulate 100 rapid-fire requests:

ab -n 100 -c 10 http://yourdomain.com/search/

Then check the logs: tail -f /var/log/nginx/error.log. If you see the message “limiting requests…”, your system is protected.

Hard-won Lessons from the Field

  1. Avoid over-tightening: A modern website can load 30-50 static files (CSS, JS, images) simultaneously. If you set the rate too low (e.g., 2r/s) without a burst, the website layout will break.
  2. Whitelisting for internal use: Always use the geo module to exclude office IPs or monitoring service IPs (UptimeRobot, Checkly).
  3. The Nginx + Fail2Ban Combo: Nginx only blocks requests at that specific moment. To permanently ban persistent IPs, use Fail2Ban to scan Nginx logs and push those IPs into the OS Firewall.

DDoS defense is an endless arms race. However, mastering Nginx Limit Request will help your server withstand 90% of common destructive bots today.

Share: