Benchmarking Nginx with wrk turns tuning changes into a repeatable baseline, so changes to workers, keepalive reuse, compression, caching, or proxy settings can be compared with numbers instead of intuition.
The wrk client opens many concurrent HTTP connections and repeatedly requests the same URL while reporting request rate and latency. The most useful runs target one stable final URL with fixed headers, because redirects, changing response bodies, or inconsistent request options make results difficult to compare across test windows.
Synthetic load can saturate the client host before it saturates Nginx, and even a short run can disturb shared environments. Use a separate load-generator host when possible, benchmark the final 200 OK URL with an explicit port and path, and include server-side checks such as stub_status plus log review so a faster result is not hiding timeouts or 4xx and 5xx responses.
Related: How to improve Nginx performance
Related: How to enable the Nginx stub_status page
Steps to benchmark Nginx with wrk:
- Choose the exact benchmark URL and confirm that it returns 200 OK without a redirect.
$ curl -I -sS http://127.0.0.1:80/index.html HTTP/1.1 200 OK Server: nginx/1.24.0 (Ubuntu) Date: Thu, 09 Apr 2026 12:43:22 GMT Content-Type: text/html Content-Length: 13 Last-Modified: Thu, 09 Apr 2026 12:43:22 GMT Connection: keep-alive ETag: "69d79eea-d" Accept-Ranges: bytes
Use the final URL that wrk will hit, including the explicit port, path, and any required Host header or authentication header. The upstream wrk usage examples also target an explicit host:port/path URL.
- Record the Nginx build or configuration under test before taking a baseline.
$ nginx -V 2>&1 | sed -n '1p' nginx version: nginx/1.24.0 (Ubuntu)
If the test compares configuration changes, save the active config snapshot separately with sudo nginx -T and sanitize sensitive upstream names, IP addresses, or file paths before sharing it.
- Confirm that the optional stub_status endpoint is reachable before relying on it during the benchmark.
$ curl -sS http://127.0.0.1:80/nginx_status Active connections: 1 server accepts handled requests 2314 2314 2288270 Reading: 0 Writing: 1 Waiting: 0
The official Nginx documentation notes that stub_status is provided by ngx_http_stub_status_module and must be present in the build. If this endpoint is not available yet, configure it first.
- Run a short warm-up so caches, open file state, and keepalive reuse settle before the timed run.
$ wrk -t2 -c20 -d3s http://127.0.0.1:80/index.html Running 3s test @ http://127.0.0.1:80/index.html 2 threads and 20 connections Thread Stats Avg Stdev Max +/- Stdev Latency 101.83us 385.35us 10.19ms 98.65% Req/Sec 127.53k 13.14k 159.77k 69.35% 785090 requests in 3.10s, 192.42MB read Requests/sec: 253247.57 Transfer/sec: 62.07MBWarm-up numbers are disposable. The goal is to stabilize the target before the saved baseline.
- Capture the baseline benchmark with latency reporting enabled and save the result to a file.
$ wrk -t2 -c20 -d8s --timeout 5s --latency http://127.0.0.1:80/index.html | tee wrk-baseline.txt Running 8s test @ http://127.0.0.1:80/index.html 2 threads and 20 connections Thread Stats Avg Stdev Max +/- Stdev Latency 273.75us 1.47ms 35.11ms 97.75% Req/Sec 93.30k 33.16k 177.34k 64.20% Latency Distribution 50% 80.00us 75% 133.00us 90% 240.00us 99% 4.89ms 1503147 requests in 8.10s, 368.41MB read Requests/sec: 185556.84 Transfer/sec: 45.48MBThe wrk command-line options documented upstream make --latency print percentile data and --timeout record stalled responses as timeouts instead of waiting indefinitely.
Do not run this kind of load test against a shared production endpoint without an approved test window, capacity headroom, and a rollback plan.
- Poll stub_status during the run or immediately after it to correlate client-side numbers with server-side connection state.
$ curl -sS http://127.0.0.1:80/nginx_status Active connections: 1 server accepts handled requests 2314 2314 2288270 Reading: 0 Writing: 1 Waiting: 0
accepts, handled, and requests show cumulative traffic, while Reading, Writing, and Waiting show the current connection mix. A sharp rise in active connections or waiting sockets during a test can point to saturation or slow upstream responses.
- Review recent HTTP status codes so a higher request rate is not hiding failures.
$ tail -n 200 /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c 200 200This summary assumes the default combined log format where the status code is field 9. Adjust the field position if the access log format has been customized.
A benchmark result is not a success if it improves Requests/sec while also increasing 4xx, 5xx, or timeout counts.
- Change one Nginx setting at a time, reload if needed, and rerun the exact same wrk command into a new result file.
$ wrk -t2 -c20 -d8s --timeout 5s --latency http://127.0.0.1:80/index.html | tee wrk-after-change.txt
Keep the URL, headers, duration, threads, and connections identical between comparison runs so the server change is the only real variable.
Related: How to improve Nginx performance
- Increase concurrency in small steps only after the baseline is stable.
$ wrk -t4 -c100 -d8s --timeout 5s --latency http://127.0.0.1:80/index.html | tee wrk-c100.txt
Jumping straight to very high connection counts can move the bottleneck to the client host, hit file descriptor limits, or flood shared upstream services before the result is useful.
- Compare saved results by focusing on request rate plus tail latency, not average latency alone.
$ grep -E "Requests/sec:|50%|90%|99%" -n wrk-*.txt wrk-baseline.txt:7: 50% 80.00us wrk-baseline.txt:9: 90% 240.00us wrk-baseline.txt:10: 99% 4.89ms wrk-baseline.txt:12:Requests/sec: 185556.84 ##### snipped #####
A lower average latency can still hide worse tail behavior. Treat the 90// and //99 percentiles as first-class comparison data when tuning Nginx.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
