> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Rate Limiting

> Demo showing configuration, testing, and tuning of NGINX request and connection rate limiting using limit_req, limit_conn, curl, ApacheBench, and log analysis.

This demo walks through practical rate-limiting on an NGINX server. We'll:

* Test a simple HTTPS site with no limits (baseline).
* Add and test request rate limiting with `limit_req`.
* Tune request limits.
* Add and test connection limiting with `limit_conn`.
* Inspect logs and tune limits based on observed traffic.

Tools used: `curl` for quick checks and `ab` (ApacheBench) to generate load and measure failed requests. Relevant NGINX directives covered: `limit_req_zone`, `limit_req`, `limit_conn_zone`, `limit_conn`.

![A diagram showing a server labeled 192.230.8.10 making multiple dashed connections to a web page (https://www.example.com) with green checkmarks for allowed requests and red Xs for blocked ones. The image is annotated with "limit\_req per ip" and "limit\_conn per ip" and includes the NGINX logo.](https://mintcdn.com/kodekloud-c4ac6d9a/ij3FNIPjp1w0Myd0/images/Nginx-for-Beginners/Performance/Demo-Rate-Limiting/nginx-limit-req-limit-conn-ip.jpg?fit=max\&auto=format\&n=ij3FNIPjp1w0Myd0\&q=85\&s=43878a93102cbb7295343c5e63b25c79)

## Environment notes / prerequisites

* NGINX is serving `example.com` (HTTPS) on `node01`. Client resolves `example.com` via `/etc/hosts`.
* You have `curl`, `ab` (ApacheBench) and shell access to the client machine.
* You can reload NGINX configuration: `nginx -t` and `nginx -s reload`.
* Read more about NGINX rate limiting in the official docs: [NGINX Rate Limiting](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html) and [limit\_conn](https://nginx.org/en/docs/http/ngx_http_limit_conn_module.html).

## 1) Initial smoke test (no rate limiting)

Verify the site responds normally before any limits are applied.

Show headers only:

```bash theme={null}
# show headers only
curl -k --head https://example.com/generic.html
```

Example response (truncated):

```text theme={null}
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 15 Feb 2025 00:02:25 GMT
Content-Type: text/html
Content-Length: 5464
Connection: keep-alive
Strict-Transport-Security: max-age=31560000; includeSubDomains; preload
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: default-src 'self'
Referrer-Policy: origin
Accept-Ranges: bytes
```

Quick loop for multiple header requests:

```bash theme={null}
for i in {1..20}; do curl -k --head https://example.com/generic.html; done
```

Run ApacheBench to establish a baseline:

```bash theme={null}
# 100 requests, concurrency 1
ab -n 100 https://example.com/
```

Sample `ab` output (summary):

```text theme={null}
Document Length:        8710 bytes

Concurrency Level:      1
Time taken for tests:   2.225 seconds
Complete requests:      100
Failed requests:        0
Requests per second:    44.94 [#/sec] (mean)
Time per request:       22.25 [ms] (mean)
```

If all requests return `HTTP/1.1 200 OK` and `Failed requests: 0`, you have a good baseline before enabling rate limiting.

## 2) Enable request rate limiting (limit\_req)

Request rate limiting controls how many requests per time unit a key (commonly an IP) can perform. Add a `limit_req_zone` in the `http` context to track requests and configure the status code to return when the limit is exceeded.

Add to `/etc/nginx/nginx.conf` inside the `http` block:

```nginx theme={null}
http {

    ##
    # Basic Settings
    ##
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Rate Limiting
    ##
    # Track requests per IP and limit to 10 requests per minute for testing
    limit_req_zone $binary_remote_addr zone=limit_per_ip:10m rate=10r/m;
    limit_req_status 429;

    ##
    # SSL Settings
    ##
}
```

Then apply the zone in a `server` or `location` block (e.g. `/etc/nginx/sites-available/example-https`):

```nginx theme={null}
server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/certs/example.com-key.pem;

    root /var/www/html;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "default-src 'self'";
    add_header Referrer-Policy origin;

    index index.html index.htm;

    location / {
        # apply request rate limiting per IP
        limit_req zone=limit_per_ip;
        try_files $uri $uri/ =404;
    }
}
```

Reload NGINX:

```bash theme={null}
nginx -t
nginx -s reload
```

### 2.1 Test request rate limiting with ApacheBench

Generate many requests from the same client IP:

```bash theme={null}
# from the client
ab -n 1000 https://example.com/
```

When the rate is very low like `10r/m`, many requests will be rejected and `ab` will report failed requests:

```text theme={null}
Concurrency Level:        1
Time taken for tests:   2.166 seconds
Complete requests:      1000
Failed requests:        999
Non-2xx responses:      999
Requests per second:    461.68 [#/sec] (mean)
```

This indicates `limit_req` is actively rejecting requests with `429`.

## 3) Tune the request rate

Adjust the `rate` in `limit_req_zone` to match realistic traffic. Example options:

* 100 requests per minute:

```nginx theme={null}
limit_req_zone $binary_remote_addr zone=limit_per_ip:10m rate=100r/m;
```

* Per-second rate:

```nginx theme={null}
limit_req_zone $binary_remote_addr zone=limit_per_ip:10m rate=100r/s;
```

After each change, run:

```bash theme={null}
nginx -t
nginx -s reload
```

Then rerun `ab` or real client tests. As the rate increases, the number of `Failed requests` should drop.

Tip: `ab` sends bursts and may exaggerate failures. Use realistic clients and concurrency to determine practical limits.

## 4) Enable connection rate limiting (limit\_conn)

Connection limiting restricts concurrent TCP connections per key (e.g., per IP). Use `limit_conn_zone` in `http` and `limit_conn` in `server`/`location`.

Add zones in the `http` block:

```nginx theme={null}
http {
    ...
    limit_req_zone $binary_remote_addr zone=limit_per_ip:10m rate=100r/s;
    limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
    limit_req_status 429;
    ...
}
```

Apply a connection limit in a `server`/`location` block:

```nginx theme={null}
server {
    listen 443 ssl;
    server_name example.com;
    ...
    location / {
        # limit to 1 concurrent connection per IP for testing
        limit_conn conn_per_ip 1;
        try_files $uri $uri/ =404;
    }
}
```

Reload NGINX:

```bash theme={null}
nginx -t
nginx -s reload
```

### 4.1 Test connection limiting with `ab`

Use `-c` to specify concurrent connections:

```bash theme={null}
# 1000 requests, 100 concurrent connections
ab -n 1000 -c 100 https://example.com/
```

Example output showing some failed requests when the connection limit is low:

```text theme={null}
Concurrency Level:        100
Time taken for tests:    0.923 seconds
Complete requests:       1000
Failed requests:         202
Non-2xx responses:       202
Requests per second:     1083.03 [#/sec] (mean)
```

If you increase `limit_conn` (for example to 100) and rerun the test, failed requests should decrease depending on how the test generates concurrent connections.

## 5) Inspect NGINX logs for limiting events

Limiting events are logged to the error log. Useful commands:

```bash theme={null}
cd /var/log/nginx
ll
tail -n 100 error.log
```

Sample `error.log` lines for request limiting:

```text theme={null}
2025/02/15 00:09:14 [error] 7219#7219: *9367 limiting requests, excess: 1.000 by zone "limit_per_ip", client: 192.231.179.3, server: example.com, request: "GET / HTTP/1.0", host: "example.com"
```

And for connection limiting:

```text theme={null}
2025/02/15 00:13:00 [error] 7341#7341: *11437 limiting connections by zone "conn_per_ip", client: 192.231.179.3, server: example.com, request: "GET / HTTP/1.0", host: "example.com"
```

Observability tips:

* Error log messages include zone name, client IP and request info.
* Use access logs to analyze normal traffic and set sensible limits by endpoint (APIs vs static assets).

## Quick reference: directives and usage

| Directive | Purpose | Example |
| - | - | - |
| `limit_req_zone` | Defines a rate-limiting zone and rate (requests per time unit) | `limit_req_zone $binary_remote_addr zone=limit_per_ip:10m rate=100r/s;` |
| `limit_req` | Applies a rate limit from a named zone in a context (e.g., `location`) | `limit_req zone=limit_per_ip;` |
| `limit_conn_zone` | Defines a connection-limiting zone keyed by variable (e.g., IP) | `limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;` |
| `limit_conn` | Limits concurrent connections by zone name | `limit_conn conn_per_ip 1;` |
| `limit_req_status` | Status code returned for rejected requests | `limit_req_status 429;` |

## Practical advice

There is no universal “magic number” for rate limits. Monitor real traffic, review logs, and tune `limit_req` and `limit_conn` gradually. Consider different limits per path (e.g., stricter limits for API write endpoints than for static assets) and exempt trusted IPs when appropriate.

## Caveats when benchmarking

Load tools like ApacheBench (`ab`) send rapid bursts and may not perfectly emulate real clients. Benchmarks can vary between runs—combine synthetic load tests with production traffic analysis before finalizing limits.

## Combined configuration example

A concise example combining both request- and connection-limiting zone declarations in the `http` context:

```nginx theme={null}
http {
    sendfile on;
    tcp_nopush on;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Rate Limiting
    ##
    limit_req_zone $binary_remote_addr zone=limit_per_ip:10m rate=1000r/s;
    limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
    limit_req_status 429;

    ##
    # SSL Settings (example)
    ##
    ssl_protocols TLSv1.2 TLSv1.3;
    ...
}
```

Adjust `rate` and `limit_conn` values to suit your traffic profile and testing results.

## Conclusion

This demo covered practical steps to protect your NGINX server from abusive request bursts and excessive concurrent connections:

* Baseline testing with `curl` and `ab`.
* Adding `limit_req_zone` + `limit_req` to limit request rates.
* Adding `limit_conn_zone` + `limit_conn` to limit concurrent connections.
* Observing limiting events in NGINX logs and tuning limits iteratively.

References:

* NGINX docs: [https://nginx.org/en/docs/](https://nginx.org/en/docs/)
* NGINX `limit_req` module: [https://nginx.org/en/docs/http/ngx\_http\_limit\_req\_module.html](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html)
* NGINX `limit_conn` module: [https://nginx.org/en/docs/http/ngx\_http\_limit\_conn\_module.html](https://nginx.org/en/docs/http/ngx_http_limit_conn_module.html)
* ApacheBench (`ab`) manual: [https://httpd.apache.org/docs/2.4/programs/ab.html](https://httpd.apache.org/docs/2.4/programs/ab.html)

That concludes this demo on rate limiting with NGINX.

> **[Watch Video](https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/4a5db5c4-df5f-4291-84f0-013d1c4ce235/lesson/19ad6278-d446-48b7-8766-86a5d6f9e015)**

> **[Practice Lab](https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/4a5db5c4-df5f-4291-84f0-013d1c4ce235/lesson/23e7ce03-5a89-453e-b956-676972047b9a)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.