Exploring rate limiting with NGINX
Posted on Thursday 6 February 2025 in Computers.
- A system container
- Load testing software
- Demonstrate the default 503 HTTP response status code
- Configure and demonstrate another HTTP response status code
Why? To better understand rate limiting in NGINX; working through this 2017 blog post: https://blog.nginx.org/blog/rate-limiting-nginx.
What? Set up an Ubuntu 20.04 LTS (Focal Fossa) container running NGINX. Load test using the artillery command-line-interface.
Prerequisites:
- Incus installed and configured, with a default profile that includes networking and storage.
- Local networking configured to integrate Incus and
resolved
.
A system container
In brief the following steps will use Incus and https://cloud-init.io/ to:
- Start a container from an Ubuntu 22.04 Focal Fossa image
- Update the local package metadata and upgrade all packages
- Install NGINX
- Serve an HTML page containing "Hello world"
- Rate limit requests to one per second
Contents of config.yaml
:
config:
user.vendor-data: |
#cloud-config
package_update: true
package_upgrade: true
packages: [nginx]
write_files:
- content: |
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
server {
listen 80;
listen [::]:80;
server_name c1.incus;
root /var/www/c1.incus;
index index.html;
location / {
try_files $uri $uri/ =404;
limit_req zone=mylimit;
}
}
path: /etc/nginx/conf.d/c1.conf
- content: |
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>Hello world</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
path: /var/www/c1.incus/index.html
Command to launch an Incus container called c1
using the above configuration:
incus launch images:ubuntu/focal/cloud c1 < config.yaml
Load testing software
The latest version of artillery —
artillery@2.0.22
— requires a specific version of Node.js: >= 22.13.0
. This
can be installed using Fast Node Manager.
Command to install the specific version of Node.js:
fnm install v22.13.1
Command to run the latest artillery:
fnm exec --using=v22.13.1 npm exec --yes artillery@2.0.22 -- --version
Demonstrate the default 503 HTTP response status code
Command to run the test:
fnm exec --using=v22.13.1 npm exec artillery@2.0.22 -- quick http://c1.incus
Partial output:
✂
http.codes.200: ................................................................ 1
http.codes.503: ................................................................ 99
http.downloaded_bytes: ......................................................... 20394
http.request_rate: ............................................................. 100/sec
http.requests: ................................................................. 100
✂
The test ran for 1 second and sent 100 requests per second for a total of 100 requests. 1 response had the 200 HTTP response status code and 99 had the 503 response status code.
Configure and demonstrate another HTTP response status code
Add another directive to the location block:
--- c1.conf
+++ c1.conf
@@ -12,5 +12,6 @@
location / {
try_files $uri $uri/ =404;
limit_req zone=mylimit;
+ limit_req_status 429;
}
}
Command to reload the NGINX configuration:
incus exec c1 -- systemctl reload nginx
Partial output from re-running the test with artillery quick
:
✂
http.codes.200: ................................................................ 1
http.codes.429: ................................................................ 99
✂
The HTTP response status codes changed from 503 to 429.
Updated 2025-02-11: use a simple test from the command line without a test script.