Introduction
You've configured rate limiting in Nginx using limit_req, but requests are not being limited or blocked. Users can make unlimited requests without being throttled, despite having limit_req directives configured. The rate limiting configuration appears correct but isn't enforcing any limits.
Symptoms
```bash # Expected behavior: requests should be limited/blocked after threshold # Actual behavior: all requests pass through
# Testing rate limit (should see 503 after threshold) $ for i in {1..100}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost/api; done 200 200 200 200 200 ... # All return 200, no rate limiting applied
# Checking limit_req status (if configured) $ curl http://localhost/status # Shows no rate limiting activity
# Nginx error log shows no rate limit messages $ grep "limiting" /var/log/nginx/error.log # No entries found ```
Common Causes
- 1.limit_req zone not referenced: Zone defined but not used in location block
- 2.burst too high: Burst allows too many requests before limiting
- 3.nodelay allows bursting: Requests burst through without delay
- 4.Whitelisted IPs: Rate limit bypassed for certain IPs
- 5.limit_req_status wrong: Using wrong status code, may not be monitored
- 6.Zone key mismatch: Using wrong variable as zone key
- 7.Zone size insufficient: Zone memory exhausted, can't track requests
- 8.Rate too high: limit_req rate allows too many requests
- 9.Location block mismatch: limit_req in wrong location block
- 10.No limit_req_zone defined: Missing zone definition
Step-by-Step Fix
- 1.Check logs for specific error messages
- 2.Verify configuration settings
- 3.Test network connectivity
- 4.Review recent changes
- 5.Apply corrective action
- 6.Verify the fix
Step 1: Verify Rate Limit Configuration
Check current rate limit setup:
```bash # Check for limit_req_zone definitions grep -r "limit_req_zone" /etc/nginx/ nginx -T 2>/dev/null | grep limit_req_zone
# Check for limit_req directives grep -r "limit_req" /etc/nginx/ nginx -T 2>/dev/null | grep limit_req
# Check zone configuration cat /etc/nginx/nginx.conf | grep -A 5 "limit_req_zone" ```
Required rate limit configuration: ```nginx # In http block (required) limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
# In server/location block (required) location /api { limit_req zone=mylimit burst=5 nodelay; } ```
Step 2: Check Zone Definition
Ensure limit_req_zone is properly defined:
# View zone definition
grep "limit_req_zone" /etc/nginx/nginx.confCorrect zone definition format: ```nginx # In http block (outside server blocks) http { # Zone definition format: # limit_req_zone KEY zone=NAME:SIZE rate=RATE
# Using client IP as key (recommended) limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# Using URI as key (for specific endpoints) limit_req_zone $request_uri zone=endpoint_limit:5m rate=5r/s;
# Using combination limit_req_zone $binary_remote_addr$uri zone=combined_limit:10m rate=5r/s;
# Rate units: r/s (requests/second) or r/m (requests/minute) # 10r/s = 10 requests per second # 600r/m = 600 requests per minute = 10r/s } ```
Common zone definition errors: ```nginx # WRONG: Zone defined inside server block server { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; # This won't work - zone must be in http block }
# WRONG: Using $remote_addr instead of $binary_remote_addr limit_req_zone $remote_addr zone=mylimit:10m rate=10r/s; # Less efficient - uses more memory
# CORRECT: Use $binary_remote_addr for IP-based limiting limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; ```
Step 3: Reference Zone in Location Block
The zone must be referenced where rate limiting should apply:
```nginx http { limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server { location /api { # WRONG: Not referencing the zone # rate limiting won't work
# CORRECT: Reference the zone limit_req zone=mylimit;
# With burst and nodelay limit_req zone=mylimit burst=5 nodelay;
# Without nodelay (smooth limiting) limit_req zone=mylimit burst=5;
# Strict limiting (no burst) limit_req zone=mylimit;
proxy_pass http://backend; }
# Different rate for different locations location /login { limit_req zone=mylimit burst=3; # More strict proxy_pass http://backend; }
location /static { # No rate limit for static files } } } ```
Step 4: Adjust burst and nodelay
Understand how burst and nodelay affect limiting:
```nginx # No burst: strict rate limiting limit_req zone=mylimit; # Exactly 10 requests/second, excess gets 503
# With burst: allows temporary burst limit_req zone=mylimit burst=5; # Allows up to 5 extra requests queued # Queued requests are delayed to match rate
# With burst and nodelay: burst without delay limit_req zone=mylimit burst=5 nodelay; # Allows burst of 5 without delay # Then strict limiting applies
# Example behavior: # rate=10r/s, burst=5, nodelay: # Request 1-15: all pass immediately (10 + 5 burst) # Request 16+: limited to 10r/s with possible 503 ```
Testing burst configuration: ```bash # Test strict limit (no burst) for i in {1..20}; do curl -s -o /dev/null -w "%{http_code} " http://localhost/api done # Should see 503s after rate exceeded
# Test with burst # With burst=5: first 15 requests (10r/s + 5 burst) pass # Request 16+ should be delayed or rejected ```
Step 5: Configure Status Code and Logging
Set appropriate status code for rate limited requests:
```nginx # Set custom status code for rate limited requests limit_req_status 429; # "Too Many Requests" (recommended) # Or limit_req_status 503; # "Service Unavailable" (default)
# Log rate limiting events limit_req_log_level notice; # Log level: emerg, alert, crit, error, warn, notice, info, debug ```
Check logs for rate limiting: ```bash # Watch for rate limit events tail -f /var/log/nginx/error.log | grep "limiting requests"
# Check existing logs grep "limiting" /var/log/nginx/error.log
# Example log entries: # [notice] limiting requests, excess: 5, by zone "mylimit" # [error] access forbidden by rule, client: 192.168.1.1 ```
Step 6: Check for Whitelisted IPs
Rate limits may be bypassed for certain IPs:
```nginx # Check for limit_req exceptions grep -r "limit_req" /etc/nginx/ | grep -v "zone="
# Whitelist configuration geo $limit { default 1; # Apply limit 127.0.0.1 0; # No limit for localhost 192.168.1.0/24 0; # No limit for internal network 10.0.0.0/8 0; # No limit for internal IPs }
map $limit $limit_key { 0 ""; # Empty key = no limiting 1 $binary_remote_addr; # Use IP for limiting }
limit_req_zone $limit_key zone=mylimit:10m rate=10r/s; ```
Verify whitelisting: ```bash # Test from whitelisted IP curl http://localhost/api # From localhost
# Test from non-whitelisted IP curl http://localhost/api # From external IP
# Check if rate limit applies differently ```
Step 7: Verify Zone Memory Size
Zone may run out of memory for tracking:
```nginx # Zone size determines how many IPs can be tracked limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
# Size calculation: # $binary_remote_addr = 4 bytes (IPv4) or 16 bytes (IPv6) # Each entry ~64 bytes overhead # 10m = 10 MB = ~160,000 IPv4 entries
# For high traffic sites limit_req_zone $binary_remote_addr zone=mylimit:50m rate=100r/s;
# Check zone usage (requires status module) location /status { limit_req_status; } ```
Step 8: Test Rate Limit Enforcement
Comprehensive testing of rate limiting:
```bash # Test script #!/bin/bash URL="http://localhost/api" COUNT=30
echo "Testing rate limit on $URL" echo "Sending $COUNT requests..."
for i in $(seq 1 $COUNT); do CODE=$(curl -s -o /dev/null -w "%{http_code}" $URL) echo "Request $i: HTTP $CODE" done
# Should see pattern like: # Request 1-10: HTTP 200 (normal rate) # Request 11-15: HTTP 200 (burst) # Request 16+: HTTP 503 or 429 (rate limited)
# Or with delays for i in $(seq 1 30); do CODE=$(curl -s -o /dev/null -w "%{http_code}" $URL) echo "$(date +%H:%M:%S) Request $i: HTTP $CODE" sleep 0.1 # 100ms between requests = 10r/s done ```
Step 9: Debug Rate Limit Configuration
Debug why rate limiting isn't working:
```nginx # Enable debug logging error_log /var/log/nginx/error.log debug;
# Or specific to rate limiting error_log /var/log/nginx/error.log notice; ```
```bash # Apply configuration sudo nginx -t && sudo systemctl reload nginx
# Watch logs during test tail -f /var/log/nginx/error.log &
# Send test requests for i in {1..20}; do curl -s http://localhost/api; done
# Look for: # "limiting requests" entries # "excess" values showing request overflow # Zone name confirmation ```
Check configuration is applied: ```bash # Dump effective configuration nginx -T 2>/dev/null | grep -A 10 "limit_req"
# Verify zone exists nginx -T 2>/dev/null | grep "limit_req_zone"
# Check location block nginx -T 2>/dev/null | grep -A 20 "location /api" ```
Step 10: Fix Common Configuration Mistakes
Correct typical errors:
```nginx # CORRECT rate limit configuration http { # Define zone in http block limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server { # Reference zone in location location /api { limit_req zone=api_limit burst=5 nodelay; limit_req_status 429; proxy_pass http://backend; }
# Different limits for different endpoints location /login { limit_req zone=api_limit burst=2; # More strict proxy_pass http://backend; }
location /search { limit_req zone=api_limit burst=3 nodelay; proxy_pass http://backend; } } } ```
Step 11: Add Rate Limit Headers
Add headers to inform clients about limits:
```nginx # Add rate limit headers to responses location /api { limit_req zone=mylimit burst=5 nodelay;
# Add headers on success add_header X-RateLimit-Limit 10 always; add_header X-RateLimit-Burst 5 always;
# Custom error page for rate limits error_page 429 = @rate_limit_exceeded;
proxy_pass http://backend; }
location @rate_limit_exceeded { default_type application/json; return 429 '{"error": "Rate limit exceeded", "retry_after": 1}'; } ```
Verification
Confirm rate limiting is working:
```bash # Quick verification #!/bin/bash for i in {1..25}; do curl -s -o /dev/null -w "Request $i: %{http_code}\n" http://localhost/api done
# Expected output pattern: # Request 1-15: 200 (rate + burst) # Request 16-25: 429 or 503 (rate limited)
# Check logs show rate limiting grep "limiting requests" /var/log/nginx/error.log
# Verify zone is tracking nginx -T 2>/dev/null | grep limit_req_zone nginx -T 2>/dev/null | grep limit_req
# Test from different IP (if using IP-based limit) curl http://localhost/api -H "X-Forwarded-For: 1.2.3.4" curl http://localhost/api -H "X-Forwarded-For: 5.6.7.8" # Each IP should have its own rate limit counter ```
Rate limiting should now enforce request limits with 429/503 responses for excess requests.
Related Articles
- [Nginx troubleshooting: Fix Lambda Permission Denied - Complete ](fix-lambda-permission-denied)
- [Nginx web server troubleshooting: Fix Client Max Body Size Large Upload Nginx Issue ](client-max-body-size-large-upload-nginx)
- [Fix Apache 502 Proxy Error](fix-apache-502-proxy-error)
- [Fix Apache LogLevel Core Debug Configuration](fix-apache-loglevel-core-debug)
- [Fix Cloudflare 502 Bad Gateway Error](fix-cloudflare-502-bad-gateway)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Nginx Rate Limit Not Working", "description": "Resolve Nginx rate limiting issues with proper limit_req zone, burst configuration, and geo module setup", "url": "https://www.fixwikihub.com/fix-nginx-rate-limit-not-triggering", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2025-11-19T20:03:32.916Z", "dateModified": "2025-11-19T20:03:32.916Z" } </script>