Skip to content

NGINX Rate Limiting — Configuring limit_req and limit_conn Modules

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about NGINX Rate Limiting. We cover key concepts, practical examples, and best practices to help you master this topic.

NGINX provides two modules for rate limiting: limit_req limits request rates using the leaky bucket algorithm, and limit_conn limits the number of concurrent connections from a single client.

What You'll Learn

  • How to configure limit_req for request rate limiting
  • How to configure limit_conn for connection limiting
  • How to define zones, set burst sizes, and configure delays

Why It Matters

NGINX is often the first layer of defense in front of application servers. Configuring rate limiting at the NGINX level protects upstream servers before requests reach your application code, providing a lightweight and high-performance first line of defense.

Real-World Use

DodaTech's API Gateway uses NGINX with rate limiting configured at the reverse proxy layer. A partner sending 1000 req/s to an endpoint limited to 100 req/s gets a 503 Service Unavailable response from NGINX before the request reaches the application server, protecting backend resources.

flowchart LR
    A["Client Request"] --> B["NGINX\nlimit_req_zone"]
    B --> C{"Within\nlimit?"}
    C -->|"Yes"| D["Application\nServer"]
    C -->|"No (burst consumed)"| E["503 Service\nUnavailable"]
    D --> F["Response"]
    style B fill:#dbeafe,stroke:#2563eb
    style E fill:#fecaca,stroke:#dc2626

Basic limit_req Configuration

# Define a shared memory zone for rate limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

# Apply to a location block
server {
    listen 80;
    server_name api.dodatech.com;

    location /api/ {
        limit_req zone=api_limit;

        proxy_pass http://backend:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Burst and Delay Configuration

# Allow bursts with a delay
limit_req_zone $binary_remote_addr zone=api_burst:10m rate=10r/s;

server {
    location /api/ {
        # burst=20 allows up to 20 extra requests in the queue
        # nodelay processes burst requests immediately, not at the limited rate
        limit_req zone=api_burst burst=20 nodelay;

        # Without nodelay, burst requests are delayed:
        # limit_req zone=api_burst burst=20;

        proxy_pass http://backend:5000;
    }
}

Multiple Zones and Conditions

# Define multiple rate limit zones for different purposes
limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=search_limit:10m rate=30r/m;
limit_req_zone $http_x_api_key zone=api_key_limit:10m rate=100r/m;

server {
    # Authentication endpoints: strict limit
    location /api/auth/ {
        limit_req zone=auth_limit burst=3 nodelay;
        proxy_pass http://backend:5000;
    }

    # Search endpoints: moderate limit
    location /api/search/ {
        limit_req zone=search_limit burst=10 nodelay;
        proxy_pass http://backend:5000;
    }

    # Apply API key-based limit globally
    location /api/v2/ {
        limit_req zone=api_key_limit burst=50;
        proxy_pass http://backend:5000;
    }
}

Connection Limiting

# Limit concurrent connections per IP
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

# Limit connections per server
limit_conn_zone $server_name zone=server_limit:10m;

server {
    location /api/ {
        # Max 10 concurrent connections per IP
        limit_conn conn_limit 10;

        # Max 100 concurrent connections total
        limit_conn server_limit 100;

        limit_conn_status 503;
        proxy_pass http://backend:5000;
    }
}

Common Mistakes

1. Using $remote_addr Instead of $binary_remote_addr

$remote_addr as a string takes more memory in the zone. Use $binary_remote_addr for IPv4 (4 bytes) or IPv6 (16 bytes) to save space.

2. Not Setting Zone Size Appropriately

A 10m zone holds about 160,000 sessions. Estimate your concurrent unique IPs and size accordingly.

3. Forgetting to Apply limit_req_status

The default status code is 503. Use limit_req_status 429 to return the standard rate limit status code.

4. Applying Limits to Internal Redirects

NGINX internal redirects (like try_files) may bypass rate limits if the limit is applied at the wrong level.

5. Not Logging Rate Limit Events

Enable rate limit logging to debug issues: add limit_req_log_level warn to see blocked requests in logs.

Practice Questions

  1. What algorithm does NGINX limit_req use?
  2. What is the difference between burst and nodelay?
  3. How do you limit by API key instead of IP?
  4. What status code should NGINX return for rate limited requests?
  5. How many sessions can a 10m zone hold?

Answers

  1. Leaky bucket algorithm. 2. Burst allows a queue of excess requests; nodelay processes them immediately instead of at the limited rate. 3. Use $http_x_api_key as the zone key variable. 4. 429 (use limit_req_status 429). 5. Approximately 160,000 sessions.

Challenge

Build an NGINX configuration that sets up different rate limit zones for authenticated and unauthenticated endpoints, uses API key extraction from headers, configures appropriate burst sizes, logs all rate limit events, and returns proper 429 status codes with Retry-After headers.

FAQ

What NGINX modules handle rate limiting?

limit_req for request rates and limit_conn for concurrent connections.

What is the difference between limit_req and limit_conn?

limit_req limits request rate (requests per second); limit_conn limits concurrent connections.

How do I return 429 instead of 503?

Use limit_req_status 429; in the server or location block.

Can NGINX rate limit by API key?

Yes. Use $http_x_api_key or $http_authorization as the zone variable.

What happens when burst is exhausted?

Additional requests receive the configured limit_req_status (default 503).

Mini Project

Create a complete NGINX rate limiting setup with: separate zones for auth (5r/m), API (30r/m), and public endpoints (100r/m), burst configurations with and without nodelay, connection limiting, proper 429 status codes, Retry-After headers, and access log formatting that includes rate limit events.

What's Next

  • Learn about Cloudflare rate limiting for edge-level protection
  • Explore AWS API Gateway rate limiting
  • Continue to distributed rate limiting with Redis cluster

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro