Load testing exposes how a web endpoint behaves under pressure, revealing throughput limits, latency spikes, and error rates before real traffic hits production.

ApacheBench (ab) is a lightweight command-line load generator that repeatedly requests a single URL path using a configurable total request count (-n) and concurrency level (-c), then reports metrics such as Requests per second, Time per request, transfer rate, and latency percentiles.

Because ab targets one URL at a time over HTTP/1.x, results do not model full browser behavior (multiple assets, HTTP/2 multiplexing, client-side rendering), and high concurrency can shift the bottleneck to the load generator host itself. Run tests only where permission exists, start conservatively to avoid accidental denial-of-service, and account for common pitfalls such as missing trailing slashes and variable response sizes.

Step-by-step video guide:

Steps to load-test web server using ab:

  1. Choose a single benchmark URL that includes a path ending in a trailing slash.
    https://www.example.com/

    URLs without a path are rejected by ab.

    $ ab -n 1 -c 1 https://www.example.com
    ab: invalid URL
    ##### snipped #####
  2. Run a small warm-up test to confirm reachability and prime caches.
    $ ab -n 50 -c 5 https://www.example.com/
    ##### snipped #####
    Complete requests:      50
    Failed requests:        0
  3. Execute a basic load test with a fixed request count and concurrency level.
    $ ab -n 1000 -c 10 https://www.example.com/
    This is ApacheBench, Version 2.3 <$Revision: 1913912 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking www.example.com (be patient)
    Completed 100 requests
    Completed 200 requests
    Completed 300 requests
    Completed 400 requests
    Completed 500 requests
    Completed 600 requests
    Completed 700 requests
    Completed 800 requests
    Completed 900 requests
    Completed 1000 requests
    Finished 1000 requests
    
    
    Server Software:        Apache/2.4.33
    Server Hostname:        www.example.com
    Server Port:            443
    
    Document Path:          /
    Document Length:        12906 bytes
    
    Concurrency Level:      10
    Time taken for tests:   19.070 seconds
    Complete requests:      1000
    Failed requests:        0
    Total transferred:      13410000 bytes
    HTML transferred:       12905610 bytes
    Requests per second:    52.44 [#/sec] (mean)
    Time per request:       190.704 [ms] (mean)
    Time per request:       19.070 [ms] (mean, across all concurrent requests)
    Transfer rate:          686.70 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    0   0.0      0       1
    Processing:    73  190  41.2    186     348
    Waiting:       72  188  41.2    185     346
    Total:         73  190  41.2    186     348
    
    Percentage of the requests served within a certain time (ms)
      50%    186
      66%    207
      75%    218
      80%    226
      90%    244
      95%    257
      98%    272
      99%    305
     100%    348 (longest request)

    Common pattern: ab -n N -c C [-k] https://host/.

  4. Focus on Failed requests and Non-2xx responses to validate correctness before comparing performance.

    Requests per second tracks throughput, while the first Time per request line is mean end-to-end latency per request.

  5. Increase -n and -c to model realistic peak traffic scenarios.
    $ ab -n 10000 -c 50 https://www.example.com/

    Large -c values can overwhelm the target and resemble a denial-of-service attack when run against production without coordination.

  6. Enable KeepAlive to measure the impact of connection reuse.
    $ ab -n 10000 -c 50 -k https://www.example.com/

    -k reuses connections where supported, reducing TCP and TLS handshake overhead.

  7. Accept variable document lengths when benchmarking dynamic pages.
    $ ab -n 2000 -c 20 -l https://www.example.com/

    Without -l, variable response sizes often appear as Failed requests: (Length).

  8. Add custom headers when a virtual host, API key, or bearer token is required.
    $ ab -n 1000 -c 20 -H 'Host: www.example.com' -H 'Authorization: Bearer REDACTED' https://203.0.113.10/

    Repeat -H for multiple headers, and use a literal IP plus Host header when DNS is not desired.

  9. Create a request body file before benchmarking a POST endpoint.
    $ cat > payload.json <<'EOF'
    {"message":"hello"}
    EOF
  10. Benchmark a POST endpoint with the body file and explicit content type header.
    $ ab -n 500 -c 10 -p payload.json -T 'application/json' https://www.example.com/api/

    POST load tests can create or modify data, so target an idempotent endpoint or a disposable test environment.

  11. Export timing and percentile data for plotting and comparisons.
    $ ab -n 5000 -c 50 -k -e percentiles.csv -g times.tsv https://www.example.com/

    -e writes a CSV percentile table and -g writes per-request timings in gnuplot-friendly format.

  12. Save the full output of a run to a file for repeatable comparisons.
    $ ab -n 5000 -c 50 -k https://www.example.com/ > ab-5000-50-k.txt
  13. Extract headline metrics from saved outputs for quick side-by-side reviews.
    $ grep -E 'Requests per second|Time per request|Failed requests|Non-2xx responses' ab-*.txt
    ab-5000-50-k.txt:Failed requests:        0
    ab-5000-50-k.txt:Requests per second:    245.19 [#/sec] (mean)
    ##### snipped #####
  14. Display the built-in help when a test needs additional flags or troubleshooting options.
    $ ab -h
    Usage: ab [options] [http[s]://]hostname[:port]/path
    Options are:
        -n requests     Number of requests to perform
        -c concurrency  Number of multiple requests to make at a time
        -t timelimit    Seconds to max. to spend on benchmarking
                        This implies -n 50000
        -s timeout      Seconds to max. wait for each response
                        Default is 30 seconds
        -b windowsize   Size of TCP send/receive buffer, in bytes
        -B address      Address to bind to when making outgoing connections
        -p postfile     File containing data to POST. Remember also to set -T
        -u putfile      File containing data to PUT. Remember also to set -T
        -T content-type Content-type header to use for POST/PUT data, eg.
                        'application/x-www-form-urlencoded'
                        Default is 'text/plain'
        -v verbosity    How much troubleshooting info to print
        -w              Print out results in HTML tables
        -i              Use HEAD instead of GET
        -x attributes   String to insert as table attributes
        -y attributes   String to insert as tr attributes
        -z attributes   String to insert as td or th attributes
        -C attribute    Add cookie, eg. 'Apache=1234'. (repeatable)
        -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: gzip'
                        Inserted after all normal header lines. (repeatable)
        -A attribute    Add Basic WWW Authentication, the attributes
                        are a colon separated username and password.
        -P attribute    Add Basic Proxy Authentication, the attributes
                        are a colon separated username and password.
        -X proxy:port   Proxyserver and port number to use
        -V              Print version number and exit
        -k              Use HTTP KeepAlive feature
        -d              Do not show percentiles served table.
        -S              Do not show confidence estimators and warnings.
        -q              Do not show progress when doing more than 150 requests
        -l              Accept variable document length (use this for dynamic pages)
        -g filename     Output collected data to gnuplot format file.
        -e filename     Output CSV file with percentages served
        -r              Don't exit on socket receive errors.
        -m method       Method name
        -h              Display usage information (this message)
        -I              Disable TLS Server Name Indication (SNI) extension
        -Z ciphersuite  Specify SSL/TLS cipher suite (See openssl ciphers)
        -f protocol     Specify SSL/TLS protocol
                        (SSL2, TLS1, TLS1.1, TLS1.2, TLS1.3 or ALL)
        -E certfile     Specify optional client certificate chain and private key
  15. Re-run the same scenarios after each server change to validate improvements and catch regressions.

    Keep the URL, payload, headers, -n / -c values, and network path consistent when comparing runs.

Tips for load-testing web server using ab:

  1. Run ApacheBench from a separate machine to avoid consuming server resources during the test.
  2. Prefer a staging or pre-production environment for high-concurrency tests.
  3. Warm up caches with a short run before collecting baseline numbers.
  4. Keep -n as a multiple of -c to distribute work evenly across concurrent workers.
  5. Use -t to cap test duration when response times are unpredictable.
  6. Use -q to suppress progress output for large request counts.
  7. Monitor CPU, memory, network, and disk I/O during the test to identify the real bottleneck.
  8. Repeat each test multiple times and compare medians to reduce the impact of transient noise.
  9. Test with and without -k to quantify the effect of connection reuse.
  10. Use -l for dynamic endpoints where Document Length varies between requests.
  11. Remember that ab does not model browser waterfalls or HTTP/2 multiplexing, so validate assumptions with additional tooling when needed.
  12. Keep detailed records of test parameters, server versions, and configuration changes for reproducible comparisons.
Discuss the article:

Comment anonymously. Login not required.