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. 1.limit_req zone not referenced: Zone defined but not used in location block
  2. 2.burst too high: Burst allows too many requests before limiting
  3. 3.nodelay allows bursting: Requests burst through without delay
  4. 4.Whitelisted IPs: Rate limit bypassed for certain IPs
  5. 5.limit_req_status wrong: Using wrong status code, may not be monitored
  6. 6.Zone key mismatch: Using wrong variable as zone key
  7. 7.Zone size insufficient: Zone memory exhausted, can't track requests
  8. 8.Rate too high: limit_req rate allows too many requests
  9. 9.Location block mismatch: limit_req in wrong location block
  10. 10.No limit_req_zone defined: Missing zone definition

Step-by-Step Fix

  1. 1.Check logs for specific error messages
  2. 2.Verify configuration settings
  3. 3.Test network connectivity
  4. 4.Review recent changes
  5. 5.Apply corrective action
  6. 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:

bash
# View zone definition
grep "limit_req_zone" /etc/nginx/nginx.conf

Correct 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.

  • [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>