ApacheBench is useful when one endpoint needs a quick, repeatable pressure test before and after a web server, application, cache, or network change. A controlled run shows whether the endpoint completed the requested work, how many requests failed, and how throughput and latency moved at a chosen concurrency level.
The ab command sends the same request until it reaches a request count with -n or a time limit with -t. Its report includes Complete requests, Failed requests, Requests per second, two Time per request lines, transfer rate, and percentile timing so matching runs can be compared without changing the URL, headers, body, or client path.
Because ab benchmarks one URL at a time and does not fully model browser traffic, HTTP/2, HTTP/3, or every HTTP/1.x response shape, treat it as a focused endpoint check rather than a full load-testing platform. Start with low concurrency, include an explicit path such as
http://www.example.net/
, and run high-load tests only on systems where the owner has approved the traffic.
Related: How to authenticate with a bearer token in cURL
Related: How to send custom headers with wget
Related: How to test Apache configuration
Steps to benchmark a web server with ApacheBench (ab):
- Choose one endpoint and include the path, even when the path is only a trailing slash.
ab rejects URLs that stop at the host name.
$ ab -n 1 -c 1 http://www.example.net ab: invalid URL Usage: ab [options] [http[s]://]hostname[:port]/path ##### snipped #####
- Run a small baseline test before raising the load.
$ ab -n 20 -c 2 http://www.example.net/ ##### snipped ##### Document Path: / Concurrency Level: 2 Complete requests: 20 Failed requests: 0 Requests per second: 5270.09 [#/sec] (mean) Time per request: 0.380 [ms] (mean)
If the baseline shows failures, redirects, authentication errors, or a different document path than expected, fix that condition before increasing concurrency.
- Run the repeatable benchmark with an explicit request count and concurrency level.
$ ab -n 1000 -c 10 http://www.example.net/ This is ApacheBench, Version 2.3 <$Revision: 1923142 $> ##### snipped ##### Server Software: Apache/2.4.66 Server Hostname: www.example.net Server Port: 80 Document Path: / Document Length: 10672 bytes Concurrency Level: 10 Time taken for tests: 0.070 seconds Complete requests: 1000 Failed requests: 0 Requests per second: 14224.55 [#/sec] (mean) Time per request: 0.703 [ms] (mean) Time per request: 0.070 [ms] (mean, across all concurrent requests) Transfer rate: 152052.65 [Kbytes/sec] received
On Debian and Ubuntu systems, /usr/bin/ab is installed by the apache2-utils package.
- Read the key report fields before changing the workload.
Requests per second is throughput for the completed run. The first Time per request line is the average latency seen by each concurrent client. The second Time per request line divides total test time across all completed requests.
- Increase -n and -c in deliberate steps after the baseline is clean.
$ ab -n 10000 -c 50 http://www.example.net/
Large concurrency values can overwhelm the target, saturate the benchmark client, or trigger rate limits. Keep production tests coordinated and reversible.
- Add KeepAlive when the scenario should reuse connections instead of opening a new TCP or TLS connection for each request.
$ ab -n 1000 -c 10 -k http://www.example.net/ ##### snipped ##### Concurrency Level: 10 Complete requests: 1000 Failed requests: 0 Keep-Alive requests: 997 Requests per second: 25001.88 [#/sec] (mean) Time per request: 0.400 [ms] (mean)
Compare runs with and without -k only when the application normally receives both connection patterns or when connection setup cost is the question being tested.
- Use a time-boxed run when each scenario should run for the same duration instead of the same request count.
$ ab -t 30 -c 20 -k http://www.example.net/
-t sets the maximum benchmark duration and internally implies a large request count, so the timer ends the run.
- Accept variable response sizes on dynamic pages with -l.
$ ab -n 2000 -c 20 -l http://www.example.net/dashboard/
Without -l, changing response length is reported as a failed length check even when every request returned an acceptable status.
- Add headers when the endpoint depends on a virtual host, token, cookie, or other request metadata.
$ ab -n 1000 -c 20 -H 'Host: www.example.net' -H 'Authorization: Bearer REDACTED' http://203.0.113.50/
Repeat -H for additional headers, use -C name=value for cookies, and use -A user:pass only for endpoints that require HTTP Basic authentication.
- Save a request body to a file before benchmarking a POST or PUT endpoint.
- payload.json
{"message":"hello"}
- Send the saved body with the matching content type.
$ ab -n 500 -c 10 -p payload.json -T 'application/json' http://api.example.net/submit
Benchmarking write operations can create, update, or duplicate data. Use an idempotent test endpoint or a disposable environment.
- Export percentile and per-request timing data when the results need plotting or later analysis.
$ ab -n 5000 -c 50 -k -e percentiles.csv -g times.tsv http://www.example.net/
-e writes percentile data as CSV, while -g writes tab-separated timing data that can be imported into plotting or spreadsheet tools.
- Save the full report for every comparable run.
$ ab -n 5000 -c 50 -k http://www.example.net/ > ab-5000-50-k.txt
Keep the URL, headers, body, concurrency, client host, and network path the same when comparing one report against another.
- Re-run the same scenarios after each server change, and use
ab -h
when additional flags such as -s, -q, or -r are needed.
Tips for benchmarking a web server with ApacheBench (ab):
- Run ab from a separate client when possible so the benchmark process does not compete with the target server for CPU, memory, or network bandwidth.
- Prefer staging, a maintenance window, or an approved production test plan for aggressive concurrency runs.
- Warm up caches with a short run before collecting the numbers that will be compared.
- Keep -n at least as large as -c, and use the same request count and concurrency when comparing two versions of the same endpoint.
- Use -q to suppress progress output during large runs and -s to lower the per-response timeout when stalled requests should fail faster.
- Use -r only when socket receive errors should not stop the run.
- Monitor CPU, memory, network throughput, and storage latency on both the benchmark client and the server to identify the real bottleneck.
- Repeat the same scenario several times and compare medians or percentile bands instead of trusting one run.
- Test with and without -k because persistent connections can materially change throughput and latency.
- Use -l only when response length legitimately changes between otherwise successful responses.
- Use broader load-testing tools when the test needs browser waterfalls, user journeys, HTTP/2, HTTP/3, think time, ramp schedules, or multiple endpoints.
- Keep a record of the exact URL, headers, payload file, request count, concurrency, client location, server version, and server-side change for every run that will be compared later.
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.