WEB SERVER
June 23, 2026

How to Configure Nginx Rate Limiting to Block Abuse

9 min read
Author
CloudStick Team
Backend Developer
Share this article
Nginx Rate Limiting
CloudStick
Nginx Rate Limiting

What Is Nginx Rate Limiting?

Rate limiting controls how many requests a single IP address can make to your server within a time window. Without it, a single bot or attacker can send thousands of requests per second to your WordPress login page, API endpoints, or contact forms — either to brute-force credentials, scrape content, or exhaust server resources. Nginx's built-in rate limiting module uses a leaky bucket algorithm, allowing a defined average request rate with a configurable burst allowance.

Rate limiting operates at the Nginx level — before requests reach PHP or your application. This means abusive traffic is rejected with a 429 response using almost no server resources, protecting PHP-FPM from being overwhelmed. A WordPress login endpoint that normally handles 50 requests per day can safely be limited to 5 requests per minute per IP with no impact on legitimate users.

Define a Limit Zone in nginx.conf

Rate limit zones are defined in the http block of nginx.conf and referenced in server blocks. A zone allocates shared memory that Nginx workers use to track request counts per IP. The rate parameter sets the average allowed requests per second or minute.

# In /etc/nginx/nginx.conf, inside http {} block
# Zone for general site traffic: 10 req/second per IP
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
# Zone for login endpoints: 5 req/minute per IP
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# Zone for API endpoints: 30 req/second per IP
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
# Return 429 Too Many Requests instead of default 503
limit_req_status 429;

Apply Rate Limits to Specific Locations

Apply zones to specific locations in your server block using limit_req. Target the endpoints most exposed to abuse: WordPress login, XML-RPC, admin-ajax, and search.

# WordPress login rate limiting
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
# Block XML-RPC entirely (unless you need it)
location = /xmlrpc.php {
deny all;
}
# Rate limit admin-ajax (common DDoS target)
location = /wp-admin/admin-ajax.php {
limit_req zone=api burst=20 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Understanding Burst and Nodelay

The burst parameter allows a queue of requests beyond the rate limit. Without burst, any request that exceeds the rate is immediately rejected. With burst=5, up to 5 extra requests are queued and processed at the permitted rate.

The nodelay flag changes burst behavior: instead of delaying burst requests until a slot is available, they are processed immediately but still counted against the quota. This is better for user experience on endpoints like login — a user clicking submit twice quickly does not wait, but a bot sending 100 requests per second still gets throttled after the burst is exhausted.

Whitelist Trusted IPs

Rate limiting applies to all clients including your own IP and monitoring tools. Use a geo or map block to exempt trusted IPs from the limit. This prevents your uptime monitor or your own office IP from triggering rate limits during testing.

# In http {} block — map trusted IPs to bypass key
geo $limit {
default 1;
127.0.0.1 0; # localhost
203.0.113.10 0; # your office IP
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
# Use $limit_key instead of $binary_remote_addr in zone:
limit_req_zone $limit_key zone=login:10m rate=5r/m;

Test That Rate Limiting Is Active

After applying your rate limit config and reloading Nginx, test by firing rapid requests at the protected endpoint. You should see 200 responses for the first few requests, then 429 responses once the burst is exhausted. Check the Nginx error log to confirm rate limit rejections are being logged.

# Fire 20 requests rapidly to test login rate limit
for i in {1..20}; do curl -o /dev/null -s -w "%{http_code}\n" -X POST https://example.com/wp-login.php; done
# Expected: first few 200, then 429s
# Check Nginx error log for rate limit messages
sudo tail -f /var/log/nginx/error.log | grep "limiting requests"
PREREQUISITE

Rate limiting protects individual endpoints but is not a substitute for a WAF or Fail2ban for brute-force protection. For complete protection, combine Nginx rate limiting with Fail2ban monitoring Nginx error logs and automatically banning IPs that trigger repeated 429 responses. CloudStick-managed servers can use this approach as part of a layered security configuration.

Leave a comment
Full Name
Email Address
Message
Contents

We use cookies to improve your experience

CloudStick uses cookies to personalise content, analyse traffic and keep you signed in. Cookie Policy · Terms of Service

Manage cookies