Hosting · WordPress · performance · infrastructure
explainer

Nginx Rate Limiting Mechanics: Configuring limit_req, Burst Queues, and Nodelay

Short answer

A technical guide to Nginx ngx_http_limit_req_module mechanics, detailing shared memory state allocation, leaky bucket rate enforcement, and the operational differences between burst queuing, nodelay execution, and two-stage delay throttling.

Research-based

Last verified:

Applies to: Nginx ngx_http_limit_req_module 0.7.21+, with delay 1.15.7+, dry-run 1.17.1+, and commercial variable-rate/sync features as documented

Comparison of Nginx burst, nodelay, and delay request handling

The Nginx ngx_http_limit_req_module regulates incoming request rates per defined key using the leaky bucket algorithm. Operating inside the http, server, and location contexts, it enables web administrators to protect application backends from bursts while defining whether excessive traffic should be queued with delays, passed immediately up to a burst ceiling, or rejected with an error status code. This guide details the exact syntax, shared memory mechanics, and scheduling options documented in official Nginx ngx_http_limit_req_module documentation.

Shared Memory Zones and the Leaky Bucket Method

Rate limiting in Nginx begins at the http context by configuring a shared memory zone with the limit_req_zone directive. This zone stores request states across worker processes, tracking the current number of excessive requests associated with each unique key.

The directive syntax is:

limit_req_zone key zone=name:size rate=rate [sync];

Key parameters and behaviors defined by the module include:

  • Key definition: The key can contain text, variables, or combinations of both (prior to version 1.7.6, keys could contain exactly one variable). Empty key values are not accounted or limited.
  • Binary IP storage: Using $binary_remote_addr instead of $remote_addr conserves memory. IPv4 addresses occupy 4 bytes, while IPv6 addresses occupy 16 bytes in binary form.
  • State footprint: Stored state occupies 64 bytes on 32-bit platforms and 128 bytes on 64-bit platforms. A 1 megabyte memory zone holds approximately 16,000 64-byte states or 8,000 128-byte states.
  • Storage exhaustion: If shared zone memory is exhausted, Nginx removes the least recently used (LRU) state. If state creation still fails after LRU eviction, the incoming request is terminated with an error.
  • Rate units: Configured in requests per second (r/s) or requests per minute (r/m). For instance, a half-request per second is written as 30r/m.
  • Commercial extensions: Dynamic variable evaluation in rate (such as mapping URIs via rate=$rate since version 1.31.3) and zone synchronization via sync (version 1.15.3) are documented features of Nginx commercial subscriptions.

Enforcing Limits: The limit_req Directive Syntax

Once a zone is defined in the http block, limits are applied within http, server, or location blocks using the limit_req directive:

limit_req zone=name [burst=number] [nodelay | delay=number];

By default, burst is 0. If incoming request rates exceed the configured zone rate, requests without an allowed burst are terminated immediately. When multiple limit_req directives are defined, Nginx processes all applicable zones. However, directives are inherited from previous configuration levels if and only if there are no limit_req directives declared at the current level.

Burst Queues, Nodelay, and Two-Stage Delay Scheduling

Nginx controls how excessive requests within the burst threshold are handled across three operational modes:

1. Default Burst Queuing (Delayed Slot Scheduling)

When only burst=number is specified, excessive requests that fit within the burst capacity are delayed and scheduled so that they drain at the configured rate:

limit_req zone=one burst=5;

If the configured rate is 1r/s and 5 requests arrive at once, the first request is served immediately, while subsequent burst requests are held and delayed until their scheduled 1-second slots open. If the incoming count exceeds the maximum burst size, excessive requests are terminated with an error.

2. Instant Execution with nodelay

If delaying excessive requests is not desired, appending the nodelay parameter allows burst requests to execute immediately without artificial delays:

limit_req zone=one burst=5 nodelay;

Under nodelay, up to 5 burst requests are dispatched to the backend immediately. However, the leaky bucket state remains marked as filled. Subsequent requests arriving before the bucket drains below the limit are rejected immediately, protecting the backend while preventing artificial client latency during legitimate micro-bursts.

3. Two-Stage Throttling with delay=number

Introduced in Nginx version 1.15.7, the delay parameter establishes two-stage rate limiting:

limit_req zone=one burst=10 delay=4;

The default value for delay is zero, meaning all excessive requests are delayed. When set to a positive integer (such as delay=4), the first 4 excessive burst requests are processed immediately without delay, while any remaining burst requests up to the total burst=10 ceiling are delayed to conform to the zone rate.

Summary of limit_req Scheduling Parameters

Configuration Burst Capacity Scheduling Behavior for Burst Traffic Excess Traffic Handling
limit_req zone=name; 0 (Default) No burst allowed; all traffic above rate is refused Terminated with configured status code
limit_req zone=name burst=5; 5 requests Queued and delayed to conform strictly to configured rate Terminated when burst capacity is exceeded
limit_req zone=name burst=5 nodelay; 5 requests Processed immediately up to burst ceiling without added delay Terminated immediately when burst slots are full
limit_req zone=name burst=10 delay=4; 10 requests First 4 burst requests pass immediately; next 6 are delayed Terminated once total burst exceeds 10

Operational Directives and Observability

The module includes additional directives for logging, error response customization, and dry-run validation:

  • limit_req_status code; (Version 1.3.15) Sets the HTTP status code returned for rejected requests. Defaults to 503 (Service Unavailable). Context: http, server, location.
  • limit_req_log_level level; (Version 0.8.18) Configures the logging level for rate-exceeded refusals (info, notice, warn, or error, defaulting to error). Delays are automatically logged at one level below refusals (for example, if notice is set, delays are logged at info). Context: http, server, location.
  • limit_req_dry_run on | off; (Version 1.17.1) Enables dry-run testing mode (defaults to off). Request processing rates are not restricted, but excessive requests are accounted for in shared memory as usual. Context: http, server, location.
  • $limit_req_status variable: (Version 1.17.6) Exposes execution status for logging or upstream inspection. Possible values are PASSED, DELAYED, REJECTED, DELAYED_DRY_RUN, and REJECTED_DRY_RUN.

Configuration Example: Multi-Zone Limiting

The following configuration limits requests per client IP while simultaneously applying a broader limit to the virtual server:

http {
    limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;
    limit_req_zone $server_name zone=perserver:10m rate=10r/s;

    server {
        server_name example.com;

        location /search/ {
            limit_req zone=perip burst=5 nodelay;
            limit_req zone=perserver burst=10;
        }
    }
}

In this block, requests to /search/ from a single IP address can burst up to 5 requests without added delay, while the virtual host permits an aggregate burst of up to 10 requests delayed according to the 10r/s server-wide rate.

Comparison of per-client and per-server Nginx rate-limit zones

Text version of the diagrams

  • Nginx Burst Modes: burst only — Excess requests are delayed; nodelay — Burst requests pass immediately; delay=4 — Early burst passes; rest delays
  • Layered Limit Zones: per-client — Keyed by binary client IP; per-server — Keyed by server name; same request — All applicable zones apply

Research Method and Documented Limitations

This article was prepared directly from public vendor excerpts of the official Nginx ngx_http_limit_req_module documentation. It makes no claims of hands-on physical laboratory benchmarks, load-testing measurements, or hardware performance timings. Feature scope is constrained strictly to the documented directives and versions: variable rates and zone synchronization (sync) are limited to commercial subscription builds, while standard open-source functionality adheres to the 64-byte/128-byte state footprint, LRU cache eviction rules, and directive inheritance boundaries specified by the primary documentation.

Related guides