Apache Bench

ab (Apache Bench) cheatsheet — benchmark web servers with -n requests, -c concurrency, -k keep-alive. Measure RPS, latency, and throughput from the command line.

7 min read

What it is

Apache Bench (ab) is a command-line tool used to measure the performance of web servers by sending a large number of concurrent requests.

Installation

Linux

sudo apt update && sudo apt install apache2-utils  # Debian/Ubuntu
sudo yum install httpd-tools                     # CentOS/RHEL/Fedora

macOS

ab is typically included with the Apache HTTP Server installation. If you have Homebrew installed:

brew install httpd

Then, the ab executable will likely be found in /usr/local/opt/httpd/bin/ab or similar. You might need to add it to your PATH.

Windows

ab is not typically installed by default on Windows. You can often find it bundled with Apache installations (e.g., from Apache Lounge or XAMPP). If you install Apache manually, the ab.exe executable will be in the bin directory of your Apache installation.

Core Concepts

ab simulates concurrent users by opening multiple connections and sending requests. The key metrics it reports are:

  • Requests per second (RPS): The number of requests the server handled per second. Higher is generally better.
  • Time per request: The total time taken for a single request, averaged across all requests. Lower is better.
  • Concurrency: The number of requests that were happening at the same time.
  • Transfer rate: The amount of data transferred per second.
  • Percentage of requests served within a certain time: This helps understand latency distribution.

Commands / Usage

Basic Benchmarking

  • Benchmark a single URL:

    ab -n 1000 http://example.com/
    

    Send 1000 requests to http://example.com/.

  • Benchmark with a specific number of concurrent connections:

    ab -c 10 -n 1000 http://example.com/
    

    Send 1000 requests with 10 concurrent connections to http://example.com/.

Controlling Requests

  • Total number of requests:

    ab -n 5000 http://localhost:8080/api/v1/users
    

    Perform a total of 5000 requests.

  • Number of concurrent requests:

    ab -c 50 -n 10000 http://192.168.1.100/
    

    Maintain 50 concurrent requests while completing a total of 10000 requests.

Controlling Time

  • Keep the benchmark running for a specific duration (instead of total requests):
    ab -t 60 -n 10000 http://example.com/
    
    Run the benchmark for 60 seconds, attempting to send up to 10000 requests. ab will stop after 60 seconds or when 10000 requests are completed, whichever comes first.

Request Methods and Data

  • Specify the HTTP method (GET, POST, PUT, DELETE, etc.):

    ab -p postdata.txt -T 'application/x-www-form-urlencoded' -t 30 -c 20 -n 5000 http://example.com/submit
    

    Send POST requests with data from postdata.txt and set the Content-Type header.

  • Send POST data from a file:

    ab -p data.json -T 'application/json' -n 100 http://example.com/api/resource
    

    Send POST requests with data.json as the body and application/json as the Content-Type.

  • Specify Content-Type:

    ab -T 'text/xml' -n 500 http://example.com/xmlservice
    

    Set the Content-Type header to text/xml.

Headers

  • Add custom headers:

    ab -H "Authorization: Bearer mytoken123" -n 200 http://example.com/secure/resource
    

    Include an Authorization header in requests.

  • Add multiple custom headers:

    ab -H "Accept-Language: en-US" -H "X-Custom-Header: myvalue" -n 200 http://example.com/
    

    Include multiple custom headers.

URLs and Redirects

  • Follow redirects (3xx status codes):

    ab -f -n 100 http://example.com/old-page
    

    The -f flag (or --follow-redirects) tells ab to follow HTTP redirects.

  • Don’t follow redirects:

    ab -n 100 http://example.com/redirecting-page
    

    By default, ab does not follow redirects.

Network and Connection Settings

  • Specify a different port:

    ab -p postdata.txt -n 500 http://localhost:8080/
    

    Send requests to localhost on port 8080.

  • Use a specific IP address (if the host has multiple):

    ab -n 100 http://192.168.1.50/
    

    Target 192.168.1.50 directly.

  • Keep connections alive:

    ab -k -n 1000 http://example.com/
    

    Use HTTP persistent connections (Keep-Alive). This is usually more realistic for modern web traffic.

  • Set connection timeout:

    ab -o 5000 -n 100 http://example.com/
    

    Set the connection timeout to 5000 milliseconds (5 seconds).

  • Set timeout for waiting for the first byte:

    ab -T 10000 -n 100 http://example.com/
    

    Set the timeout for waiting for the first byte of the response to 10000 milliseconds (10 seconds).

Output and Reporting

  • Disable timer information in output:

    ab -i -n 100 http://example.com/
    

    The -i flag (or --timings) disables the printing of percentage timed out information.

  • Print each request’s timing:

    ab -e results.csv -n 1000 http://example.com/
    

    Write detailed per-request timing information to results.csv.

  • Output results in CSV format:

    ab -e output.csv -n 1000 http://example.com/
    

    Save a summary of the benchmark results to output.csv.

  • Use a specific URL for each request (from a file):

    ab -D url_list.txt -n 1000 http://example.com/
    

    Read URLs from url_list.txt and use each one for requests. This is useful for testing a mix of endpoints.

Other Options

  • Verbose output:

    ab -v 2 -n 100 http://example.com/
    

    Set verbosity level. Level 2 shows response headers.

  • Disable "Connection: close" header:

    ab -S -n 100 http://example.com/
    

    The -S flag (or --disable-keepalive) disables the Connection: close header, which is often sent by servers. This is less common than -k.

  • Specify the user agent:

    ab -A "MyBenchmarkTool/1.0" -n 100 http://example.com/
    

    Set the User-Agent header.

Common Patterns

  • Benchmarking a POST endpoint with JSON data:

    echo '{"key": "value"}' > payload.json
    ab -p payload.json -T 'application/json' -c 20 -n 1000 http://localhost:3000/api/items
    
  • Testing a static file download with Keep-Alive:

    ab -k -n 500 http://your-cdn.com/large-file.zip
    
  • Simulating load on a specific IP and port:

    ab -c 100 -n 5000 http://10.0.0.1:8080/status
    
  • Comparing performance with and without Keep-Alive:

    echo "--- Without Keep-Alive ---"
    ab -n 1000 http://example.com/
    
    echo "--- With Keep-Alive ---"
    ab -k -n 1000 http://example.com/
    
  • Generating a CSV report for analysis:

    ab -e report.csv -c 50 -n 10000 http://example.com/data
    

Gotchas

  • ab is a client, not a load generator: ab runs on a single machine. If the machine running ab becomes the bottleneck (CPU, network), your results will be inaccurate and won’t reflect the server’s true capacity. For serious load testing, use distributed load generators.
  • Client-side overhead: The machine running ab consumes resources. High concurrency can saturate its CPU or network interface, artificially limiting the number of requests that can be sent.
  • ab vs. curl: ab is for load testing multiple requests. curl is for single request interaction and debugging. Don’t use ab to inspect headers of a single request.
  • Keep-Alive (-k) is important: Most modern web servers and clients use HTTP Keep-Alive by default. Benchmarking without -k might not reflect real-world performance. However, if your server is configured to close connections aggressively, testing without -k might be appropriate.
  • URL interpretation: ab sends requests to the exact URL provided. If you specify http://example.com/, it will hit that specific path. If you want to test multiple paths, you need to run ab multiple times or use the -D flag with a file containing multiple URLs.
  • POST data size limits: When sending POST data, ab itself doesn’t have a strict limit, but the server’s request body size limits will apply.
  • IPv6: ab supports IPv6, but you might need to specify the address in square brackets, e.g., http://[::1]/.
  • Error handling: ab reports connection errors and timeouts, but it doesn’t parse the response body for application-level errors (e.g., HTTP 500 status codes). You need to check the "Non-2xx responses" count in the output.