Stress-test your PHP App with ApacheBench

Bruno Skvorc

This article was sponsored by New Relic. Thank you for supporting the sponsors that make SitePoint possible!

There’s no telling when your app might attract a throng of visitors at once – maybe it’s a Hacker News post that’s submitted at a specific second on a specific time of day (as posts there tend to work), maybe it was a particularly well placed Reddit post, and maybe it’s actually good and people noticed it, spreading it virally. Regardless of the reason, massive influxes of visitors are a double-edged sword: they get you what you always wanted – a chance to prove your worth to a large chunk of the internet’s population – but also often bring with them what you always feared: absolute downtime.

There are several ways to try and prevent this – among the most prevalent is deploying your application on a service like Amazon, Google App Engine or Heroku which have the ability to not only scale out and in rapidly at your command, but also support automatic scaling, if you’re not afraid of DDoS attacks also scaling your bills while you’re asleep. These platforms usually offer plugins that can optimize your application while it’s up, so you can fine tune it as you go along, but why not try and predict issues while still developing locally and save yourself time, money and effort in the long run?

Apache Benchmark

ApacheBench (also known as “ab”, the command you run it with) is a tool designed to nuke an endpoint with requests and load-test web servers. It supports a wide array of parameters and options you can tweak to simulate different loads, like number of requests, number of concurrent requests, extra headers, falsified cookies, and more.

ab is often included with every Apache installation, but if not, you can easily install it by running sudo apt-get install apache2-utils. The current version of Laravel Homestead doesn’t have it, so if you’re following along please install it into that VM when you vagrant up and connect through SSH.

You can verify you have it installed by just running ab, which should produce a list of supported options:

For our demonstration, I created a subfolder Laravel under Code in the VM, added a subfolder called “public”, and put an index.php file inside with the following content:


echo "Hello Test";

In homestead.yaml, I called this site “”, and in my hosts file on my host machine I added This allows me to run sites hosted on the Homestead VM from the host machine’s browser via, all standard stuff we covered before. However, this also allows you to curl from inside the VM – something we can take advantage of when bombing our app with ab.

Test run

For starters, let’s try and nuke our with a sample ab grenade – just run ab on the URL, without any options. Note that you must follow the URL with a slash, otherwise you’ll get an invalid URL error from ab.


We can see the benchmark finishes almost instantly – the number of requests per second and concurrency load is too unreal and simple to yield useful results. In fact, running the bench multiple times will produce results from 50 to 200 possible requests per second. To provide some more tangible results, we need to up our game.

Demo app

Go outside the Laravel folder and remove it entirely with rm -rf Laravel. Next, we’ll create a default sample Laravel application with all dependencies. If you don’t have Composer installed globally yet, do it, it’s easy and fast. Then, run the following command:

composer create-project laravel/laravel Laravel --prefer-dist

This will download the Laravel skeleton, all dependencies, and generate the autoload and lock files that are needed. You can verify it works by going to on your host machine, or curling inside the VM:

Let’s tweak the basic bench command and properly step on our app’s toes. Run ab -n 500 -c 100 to make a more realistic test. The n param means “number of requests”, while c is concurrency, as in, how many requests at a time. Thus, this command will execute 5 batches of 100 simultaneous requests.

The default result format you get at the end of the output will tell you how many requests (in percentage) were executed within a given timeframe, 10 by 10 per cent from 50% to 100%. In other words, if the first row is 50% 3200, that means half the total requests were executed within 3.2 seconds. For 250 requests, this isn’t bad, especially on such a weak machine.

Let’s try and slow things down on purpose. Go into app/controllers/HomeController.php and change the showWelcome function to this:

    public function showWelcome()

        if (isset($_GET['slower']) && $_GET['slower'] == 'true') {
        } else {

        return View::make('hello');

If you’d like to get some more consistent results, feel free to remove the call to Google Fonts from hello.php, as that removes the downloading lag when fetching the font and leaves everything in the hands of the VM, locally. Also, in the routes.php file, replace the current closure based route with this:

Route::get('/', 'HomeController@showWelcome');

Now try benching two different URLs: and As expected, you’ll notice the results differ wildly. This was a completely ridiculous example, but it’s enough to demonstrate the effect a long running script can have on your visitors when they flock in en-masse. If you have long running scripts like these, it’s much better to delegate them to the background via message queues or other means, and make sure your client-facing script is as fast as you can make it.


In this introduction to ApacheBench, we saw how much of an effect script efficiency had on huge traffic surges. We’ll be adding more ab tutorials further down the line, but in the meanwhile, all you should take away from this is – don’t underestimate small optimizations. There is the potential of premature optimization, but if you can detect and remove the boulders from the path sooner, the road will be smoother in the long run. Play around with ab, test it out, switch out parameters and options, add database connections to the sample Laravel app we played around with – break it, fix it, break it again. Let us know what you find – we’d love to publish your advanced use cases!