🎉 GoReplay is now part of Probe Labs. 🎉

Published on 7/11/2026

A Practical Guide to Load Testing with Ab Apache Bench

- A photo-realistic server room with racks of blinking LED servers in soft focus, featuring "ApacheBench" text as the central focal point on a solid background block at the golden ratio position, surrounded by subtle network graphs and terminal windows, brand & text realism style

Jumping into load testing can feel overly complex, but the classic ab (Apache Bench) tool makes it surprisingly straightforward.

Getting Started with Your First ab Apache Bench Test

Load testing is the only real way to understand how your web app or API holds up under pressure. You can’t fix performance bottlenecks until you find them, and that’s exactly where a simple but powerful command-line tool like ab shines. Its job is to hammer a web server with a specific number of concurrent requests, giving you an instant baseline of its capacity.

Apache Bench has been around forever. It was originally built to test the Apache HTTP Server, but its sheer simplicity made it a go-to tool for basic load testing across the board. In a typical run, ab can fire off thousands of HTTP requests and spit back core metrics like requests per second, time per request, and how many requests failed. If you want a deeper dive into its history, Liquid Web has a great guide on benchmarking with Apache Bench.

A laptop screen displaying 'AB -N 100 -C 10' and 'htTF your ste com' with 'RUN AB TEST' overlaid on the desk.

Installing the Necessary Tools

First things first, you need to get ab on your machine. It usually comes bundled with apache2-utils (or httpd-tools on some systems), so installation is typically just a single command.

  • Debian/Ubuntu: sudo apt-get update && sudo apt-get install apache2-utils
  • CentOS/RHEL: sudo yum install httpd-tools or sudo dnf install httpd-tools
  • macOS: If you use Homebrew, it’s just brew install httpd.
  • Windows: The easiest way is to grab an Apache server package like XAMPP. You’ll find ab.exe tucked inside its apache\bin folder.

Once that’s done, pop open a terminal and type ab -V. If it prints out the version info, you’re all set.

Running Your First Basic Test

With ab installed, you can run your first test right away. The syntax is dead simple: you just specify the total number of requests, the concurrency level, and the URL you want to hit.

Let’s try a simple test sending 100 total requests with 10 users hitting the server at the same time.

ab -n 100 -c 10 http://your-site.com/

Here’s what that command is actually doing:

  • -n 100: This tells ab to send a grand total of 100 requests.
  • -c 10: This sets the concurrency to 10, meaning ab opens up 10 simultaneous connections to fire off requests.
  • http://your-site.com/: This is your target. It’s always a good idea to start with a simple, static page to get a clean baseline.

Important Tip: This is critical. Never run ab from the same machine you are testing. The test itself consumes CPU and memory, which will contaminate your results and give you a completely misleading picture of your server’s actual performance. Always test from a separate machine.

How To Interpret Ab Apache Bench Results

Running an ab test is the easy part. The real skill is turning that dense wall of text into a clear picture of what your server is doing under pressure. It can look intimidating at first, but you really only need to zero in on a handful of key metrics to get the story.

Let’s break down the important numbers and what they signal about your application’s health.

A computer monitor displays a data interpretation graph with 'Interpret Results' text, keyboard, and mouse.

The first number everyone gravitates toward is Requests per second. This is your server’s raw throughput—the higher, the better. It tells you exactly how many requests your server successfully chewed through each second during the test. If this number is low, or worse, if it drops as you dial up the concurrency, that’s a classic sign your server is hitting a wall.

Next up is Time per request. This metric usually comes in two flavors. The first value shows the average time across all concurrent requests, but the second one (the mean) is often more telling because it represents the average wait time a real user would experience. If this number is high, your users are feeling the pain of slow load times.

Digging Deeper Into The Numbers

Beyond those headline figures, the detailed connection and timing breakdowns paint a much more nuanced picture. ab gives you a specific section on connection times and another that shows the percentage of requests served within a certain time.

These details are gold for pinpointing the source of a bottleneck. For example, a long “Connecting” time might scream network latency or a firewall issue. A long “Processing” time, on the other hand, points the finger directly at your application code or a slow database query.

A high standard deviation in your response times is a major red flag. It means your server’s performance is erratic. Some users might get a zippy response, while others are left staring at a loading spinner. This often signals resource contention or garbage collection pauses getting in the way.

What to Look For In The Output

To make this super practical, here’s a breakdown of the critical lines in a typical ab report and what they actually mean.

I’ve put together a quick reference table to help you decode the output at a glance. These are the metrics I always check first.

Key Metrics in Apache Bench Output

MetricWhat It MeansWhat to Look For
Requests per secondThe total number of requests your server handled per second. This is your core throughput metric.A higher number is better. Watch for this to decrease as concurrency (-c) increases.
Time per requestThe average time a single request took to complete from start to finish.A lower number is better. This directly translates to user-perceived latency.
Failed requestsAny requests that did not complete successfully.This should be zero. Any failures indicate your server is overloaded and dropping connections.
99% (ms)The time within which 99% of all requests were completed.This is more valuable than the average, as it shows the worst-case experience for most users.

Focusing on these specific metrics helps you move from raw data to a clear diagnosis quickly. A high number of Failed requests combined with slow request times under moderate concurrency tells you the server is at its breaking point. This is the kind of data-driven insight you need to make smart decisions about scaling your infrastructure or optimizing your code.

Simulating Realistic Traffic with Advanced Commands

Simple GET requests are a great starting point, but they don’t tell the whole story. Real users interact with applications in complex ways—they fill out forms, log in, and maintain sessions. To uncover true performance limits, your ab tests must mirror this reality.

This is where ab’s more powerful flags come into play. Moving beyond basic commands allows you to simulate these scenarios and stress-test the dynamic parts of your application, not just its static front page. By crafting commands that send POST data, include custom headers, and maintain connections, you can build much more insightful load tests that reflect genuine user behavior.

A laptop displaying code, a smartphone, and a book titled 'Simulate Traffic' on a wooden desk.

Testing Forms and APIs with POST Requests

Many of your most critical application workflows, like user logins or data submissions, rely on POST requests. Thankfully, ab gives us two essential flags to test these endpoints: -p to specify a file containing the POST data and -T to set the Content-Type header.

Let’s say you want to load test a login API that expects a JSON payload. First, you’ll need to create a file, maybe login-payload.json, with the content:

{ “username”: “testuser”, “password”: “securepassword123” }

With that file ready, you can run ab with the -p and -T flags to hammer your login endpoint.

ab -n 200 -c 20 -p login-payload.json -T 'application/json' https://api.your-site.com/v1/login

This command simulates 20 concurrent users trying to log in, sending a total of 200 requests. It’s a fantastic way to find out how your authentication service and database hold up under a sudden surge of simultaneous login attempts.

Simulating Authenticated Users and Sessions

Modern apps are rarely wide open; they require authentication. To test endpoints behind a login wall, you need to mimic an authenticated user by sending the correct session cookie or authorization token. The -H flag for custom headers is perfect for this.

For example, if your application uses a bearer token for API authentication, you can include it like this:

ab -n 500 -c 25 -H "Authorization: Bearer your-jwt-token-here" https://api.your-site.com/v1/dashboard

Similarly, you can use the -C flag to pass a session cookie, which is common in more traditional web apps:

ab -n 500 -c 25 -C 'sessionid=abc123xyz' https://app.your-site.com/profile

These techniques let you bypass the login page and directly test the performance of protected resources. This is crucial because performance bottlenecks often hide in the parts of your application that do the most work—the parts only logged-in users can access.

Apache Bench is a fantastic tool for generating simple, repetitive load. However, it can’t replicate complex, multi-step user journeys. For a more sophisticated approach, you might want to learn how to replay production traffic for realistic load testing, which provides a much more accurate simulation of real-world usage patterns.

The Importance of Keep-Alive Connections

By default, ab opens a brand new TCP connection for every single request. That’s not how modern browsers operate at all. Browsers use persistent connections (Keep-Alive) to send multiple requests over the same connection, which is far more efficient.

You can—and should—instruct ab to do the same with the -k flag.

ab -k -n 1000 -c 50 http://your-site.com/

Enabling Keep-Alive gives you a much more realistic benchmark of a modern web server’s performance. It slashes the overhead of constant TCP handshakes, giving you a truer measure of your application’s response time rather than network latency. If you forget to use -k, you might see an artificially low “Requests per second” metric because your server is spending too much time just opening and closing connections.

Common Pitfalls in Load Testing and How to Avoid Them

Using a tool like ab apache bench gives you a fast and powerful way to peek under the hood of your server’s performance. But it’s surprisingly easy to fool yourself with misleading results. Getting data you can actually trust isn’t just about running commands—it’s about sidestepping the common traps that can completely invalidate your tests. The biggest mistake? Assuming the numbers are always telling you the whole story.

A classic blunder is running ab on the same machine that’s hosting the application you’re testing. This is the cardinal sin of load testing. The test itself eats up CPU, memory, and network resources, creating a competition that will inevitably bog down your server and make its performance look far worse than it really is. Always run your load tests from a separate, dedicated machine to keep your measurements clean.

A computer display and keyboard on a wooden counter with a blue 'AVOID Pitfalls' sign.

The Coordinated Omission Problem

There’s a more subtle issue that can quietly wreck your results: coordinated omission. This happens because ab waits to get a response before it sends the next request on the same thread. If your server hangs or just takes a long time to answer, ab simply waits. That “waiting time” isn’t properly counted as a service failure in the final stats.

This can dangerously flatter your numbers. The tool essentially pauses the clock during hiccups, hiding the true impact of long response times on your users. Your average latency might look great, but in reality, people could be hitting significant delays that ab isn’t even capturing.

Key Takeaway: Coordinated omission can make an unstable server look deceptively healthy. Be skeptical of the latency figures from ab, as they can be overly optimistic, especially when the server is struggling under heavy load.

Network Latency and Single-Threaded Limits

Another common trap is confusing network latency with your server’s actual processing time. If you’re testing a server in another country, a big chunk of the request time might just be the round-trip delay. Before you start, run a simple ping to get a baseline for network latency. Subtracting that from your ab results will give you a much better estimate of your server’s true performance.

It’s also crucial to remember that ab is single-threaded. It simply can’t generate a massive amount of traffic from one instance, especially if you’re running it on a modern multi-core machine. If your goal is to simulate tens of thousands of concurrent users, you’ll need to either run multiple instances of ab at the same time or switch to more advanced, distributed load testing tools.

The open-source performance testing market is growing fast, with an estimated compound annual growth rate (CAGR) of about 12.3%. Among the more than 50 different testing tools out there, Apache Bench still holds its ground because it’s so lightweight and easy to use for quick, standalone benchmarks. You can discover more insights about the open-source testing market to see where it fits in the broader ecosystem.

Integrating Ab into Your Development Workflow

Performance testing isn’t just a one-off event. You get the most value when it’s a consistent, automated part of your development cycle. By weaving ab apache bench into your CI/CD pipeline, performance stops being an afterthought and becomes a core quality gate.

This proactive approach means you catch performance regressions the moment they’re introduced—not weeks later when a user complains.

By embedding ab commands directly into your deployment scripts, you can automatically benchmark your application after every successful build. This creates a tight feedback loop where every code change is immediately checked against a performance baseline. It’s the difference between hoping your application is fast and knowing it is.

Automating Ab in Your CI Pipeline

Tools like GitHub Actions or GitLab CI make this kind of automation surprisingly straightforward. The idea is to configure a job that kicks off right after your deployment step. Its main task? To run a predefined ab test against your newly deployed environment.

This process breaks down into a few key actions:

  • Install Utilities: Make sure the CI runner has apache2-utils or httpd-tools installed to get access to the ab command.
  • Run the Test: Execute an ab command targeting a critical endpoint, like your homepage or a key API.
  • Capture the Output: Pipe the results into a file or variable so you can parse them in the next step.

For instance, a simple step in a YAML configuration file might look something like this:

ab -n 500 -c 25 https://staging.your-api.com/health | tee results.txt

This command runs a moderate load test and saves the output to a file, which sets the stage for automated analysis.

Establishing Performance Budgets

Once you’re capturing results, you need to make them actionable. Manually checking the output after every CI run just doesn’t scale. The real power comes from scripting a check that parses the ab output to extract the most important metric: Requests per second.

You can use simple shell commands like grep and awk to pull this number right from the text output. For example, grep "Requests per second" results.txt | awk '{print $4}' will isolate the exact value you need.

With this metric in hand, you can establish a performance budget—a minimum acceptable performance threshold. Your script can then compare the latest test result against this budget.

If the “Requests per second” metric dips below your defined baseline (say, 150 req/s), the pipeline fails. This hard stop is crucial because it prevents performance regressions from ever making it to production.

This method of integrating ab creates a powerful safety net. It’s a great starting point, but you can explore more advanced strategies for building out this capability. For a deeper dive, check out this guide on setting up continuous performance tests.

Implementing this kind of automated workflow ensures every single commit is held to a high performance standard, keeping your application consistently fast and reliable for your users.

Answering Your First Questions About ApacheBench

When you’re first getting your feet wet with ab, a few questions almost always come up. Getting these sorted out early will save you a ton of guesswork and help you build much better performance tests.

Let’s dive right into the most common ones. Nailing these concepts is the key to going from running basic commands to actually understanding what the results mean.

What’s the Difference Between Concurrency and Total Requests?

This is easily the most common point of confusion. The best way to think about it is like running a busy coffee shop.

  • Concurrency (-c) is how many baristas you have working at the same time. A -c 10 means you have 10 baristas all making coffee simultaneously. It’s all about simultaneous pressure.
  • Total Requests (-n) is the total number of customers you need to serve for the entire test. A -n 1000 means 1,000 customers will get their coffee before the test is over.

So, when you run ab -n 1000 -c 10, you’re telling 10 baristas to work in parallel until they’ve collectively served 1,000 customers. In this scenario, each barista handles 100 customers, one after the other. Cranking up the concurrency is how you really find out if your server can handle a rush.

Pro Tip: Your server has a breaking point. If you set -c too high, you’ll start seeing a flood of failed requests. The trick is to start low and gradually increase the concurrency to find out just how much your application can take before it falls over.

Can Ab Test Dynamic Pages with User Sessions?

Yes, but it has its limits. You can definitely simulate a logged-in user for an entire test run by passing session data along with your requests. This is done using the -C flag for cookies or the -H flag for authentication headers.

For instance, to pass a session cookie for every request: ab -n 200 -c 10 -C 'sessionid=your_session_id' https://app.yoursite.com/dashboard

Here’s the catch: ab is stateless. It can’t handle complex, multi-step user journeys where things change from one request to the next, like adding an item to a shopping cart and then proceeding to checkout. Every single request it sends is independent. For those more advanced, stateful scenarios, you’ll need to reach for a more sophisticated tool like JMeter.

Why Am I Seeing So Many Failed Requests?

A spike in failed requests is a classic sign that your server is buckling under the pressure. The ab output usually drops a hint in the “(Connect, Receive, Length, Exceptions)” section.

  • Connect failures: The server flat-out refused the connection. This often means it’s hit its maximum connection limit.
  • Length failures: The server sent something back, but the response size didn’t match what ab saw from the very first successful request. This is a dead giveaway that the server is returning an error page instead of your actual content.
  • Receive failures: The server accepted the connection but then ghosted ab, failing to send a response in time.

If you see any of these, your first move should always be to dial back the concurrency level. Then, go dig through your server logs; they’ll almost always have corresponding error messages that tell you the real story.


While ApacheBench is fantastic for a quick health check, simulating real-world production traffic demands a more powerful approach. GoReplay is an open-source tool built to capture and replay your actual user traffic, letting you test with a level of realism ab can’t match. Find out how you can safely validate your changes before they ever see the light of day at https://goreplay.org.

Ready to Get Started?

Join these successful companies in using GoReplay to improve your testing and deployment processes.