Apache Benchmarks with ab and curl
In web server administration there is often a need to measure the performance of a website. Especially after updates to components such as Apache or PHP, there is a possibility that a well-functioning website may suddenly lose performance.
Two tools can help here: ab (ApacheBench) for load testing with multiple
parallel requests, and curl for quick single-request timing. Both run on the
command line and produce results immediately.
ab
ab is part of the apache2-utils package:
sudo apt install apache2-utils
It sends a configurable number of requests to a URL β sequentially or in
parallel β and produces a statistical report. The number of requests is set
with -n, concurrency with -c.
A basic run with 5 sequential requests:
ab -n 5 <url>
Example output:
This is ApacheBench, Version 2.3 <$Revision: 1923142 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking <Servername> (be patient).....done
Server Software: Apache
Server Hostname: <Servername>
Server Port: 80
Document Path: /subdir
Document Length: 133099 bytes
Concurrency Level: 1
Time taken for tests: 7.824 seconds
Complete requests: 5
Failed requests: 0
Total transferred: 669680 bytes
HTML transferred: 665495 bytes
Requests per second: 0.64 [#/sec] (mean)
Time per request: 1564.802 [ms] (mean)
Time per request: 1564.802 [ms] (mean, across all concurrent requests)
Transfer rate: 83.59 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 1480 1565 64.9 1600 1631
Waiting: 1391 1468 58.1 1507 1515
Total: 1480 1565 64.9 1600 1631
Percentage of the requests served within a certain time (ms)
50% 1591
66% 1608
75% 1608
80% 1631
90% 1631
95% 1631
98% 1631
99% 1631
100% 1631 (longest request)
The most relevant lines for a quick comparison:
| Field | Meaning |
|---|---|
Time taken for tests |
Total wall-clock time for all requests |
Requests per second |
Throughput β higher is better |
Time per request |
Average latency per request |
Failed requests |
Any value above 0 deserves investigation |
Waiting |
Time until first byte β reflects server processing time |
50% / 95% / 100% |
Latency percentiles β the 95% and 100% values reveal outliers |
Parallel requests
To simulate concurrent users, add -c for the concurrency level. The total
number of requests -n should be a multiple of -c:
ab -n 100 -c 10 <url>
This sends 100 requests with 10 running in parallel at any time β a useful stress test to find out how the server behaves under load.
HTTPS and custom headers
For HTTPS URLs ab uses the https:// scheme directly. To add a custom
header (e.g. for authenticated endpoints):
ab -n 50 -c 5 -H "Authorization: Bearer <token>" <url>
curl
curl is well suited for quick single-request timing, particularly when
comparing the effect of a specific change. The -w flag controls what gets
printed after the request completes; -o /dev/null -sS suppresses the
response body while still showing errors.
The most useful single metric:
curl -o /dev/null -sS -w "%{time_total}\n" <url>
For a more detailed breakdown, use a multi-line format string:
curl -o /dev/null -sS -w "
namelookup: %{time_namelookup}s
connect: %{time_connect}s
appconnect: %{time_appconnect}s
pretransfer: %{time_pretransfer}s
starttransfer: %{time_starttransfer}s
----------
total: %{time_total}s
" <url>
| Field | Meaning |
|---|---|
time_namelookup |
DNS resolution time |
time_connect |
TCP connection established |
time_appconnect |
TLS handshake complete (HTTPS only) |
time_pretransfer |
Time until request was sent |
time_starttransfer |
Time until first byte received (TTFB) |
time_total |
Full transfer including response body |
time_starttransfer (TTFB β Time To First Byte) is especially useful: it
reflects server processing time and is not affected by the size of the
response.
Averaging multiple measurements
A single curl measurement can vary due to network jitter or caching. Running several requests in a loop gives a more reliable picture:
for i in $(seq 1 10); do
curl -o /dev/null -sS -w "%{time_total}\n" <url>
done
Conclusion
Both tools complement each other well. ab is the right choice when you need
to test throughput and behaviour under load β after a configuration change or
to find the concurrency limit of a server. curl is faster to use for a quick
before/after comparison of a single endpoint.
Either way, having baseline values documented before making changes is worth the effort. Without a reference point, it is difficult to judge whether a change actually improved anything.