Rate Limiting and Connection Limits

Introduction

Public endpoints attract bots and accidental traffic spikes. Nginx can limit request rate and concurrent connections per IP before requests hit your Java or Node process. This chapter sets up limit_req and limit_conn for login and API paths—complement, not replacement, for app-level throttling.

Prerequisites

limit_req: Request Rate

Define a shared memory zone in http context:

nginx
http {
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
 
    server {
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            proxy_pass http://127.0.0.1:8080/;
            include /etc/nginx/proxy_params;
        }
    }
}

Code explanation:

  • $binary_remote_addr — client IP as zone key
  • zone=api_limit:10m — store state in 10 MB shared memory
  • rate=10r/s — average 10 requests per second per IP
  • burst=20 — allow short bursts up to 20 excess requests
  • nodelay — process burst immediately instead of queuing with delay

Exceeded limit returns 503 by default (or 429 with limit_req_status 429;).

Login endpoint stricter limit

nginx
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
 
location /api/login {
    limit_req zone=login_limit burst=5 nodelay;
    proxy_pass http://127.0.0.1:8080;
}

limit_conn: Concurrent Connections

nginx
http {
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
 
    server {
        limit_conn conn_limit 20;
 
        location / {
            proxy_pass http://127.0.0.1:8080;
        }
    }
}

Code explanation:

  • Max 20 simultaneous connections per IP for this server
  • Helps slowloris-style abuse; tune for legitimate heavy users

Custom Status Code

nginx
limit_req_status 429;
limit_conn_status 429;

Clients and monitors recognize Too Many Requests.

Whitelist Internal IPs

nginx
geo $limit {
    default 1;
    127.0.0.1 0;
    10.0.0.0/8 0;
}
 
map $limit $limit_key {
    0 "";
    1 $binary_remote_addr;
}
 
limit_req_zone $limit_key zone=api_limit:10m rate=10r/s;

When $limit_key is empty string, zone key is empty—advanced pattern to skip limits for trusted nets.

Combine with Security Headers

Rate limiting at edge; still validate auth in Spring Boot / Express. Do not expose admin tools without IP allow and app auth.

Testing Limits

bash
for i in $(seq 1 50); do curl -s -o /dev/null -w '%{http_code}\n' http://api.example.com/api/ping; done

Expect mix of 200 then 429 or 503 when exceeding rate.

Watch error log:

bash
sudo tail -f /var/log/nginx/error.log

May log limiting notices depending on level.

Production Considerations

TopicNote
Shared memoryZone size (10m) holds many IP entries— increase if huge traffic
CDN in frontRate limit by $http_x_forwarded_for or CDN header—careful spoofing
NAT usersMany users share one IP—limits may be too aggressive
API keysPrefer app-level quotas per key; IP limit is coarse

Warning

Do Not Rely on IP Alone

Mobile carriers and corporate NAT bundle many users—combine with auth and app throttles.

FAQ

limit_req in location vs server?

Zone defined in http; apply limit_req in server or location as needed.

Burst without nodelay?

Excess requests queued with delay—smoother but slower under spike.

Multiple zones?

Different zones for /api/ vs /login with different rates.

limit_req and HTTP/2?

Works per request; HTTP/2 multiplexing still counts requests.

Alternative tools?

Cloud WAF, API gateway, Redis rate limit in app—stack as needed.

503 vs 429?

Default 503 for limit_req—set limit_req_status 429 for clearer semantics.