Employee Post

Setting up WordPress + Nginx + HHVM For The Fastest Possible Load Times

One of the most enjoyable parts about writing code is writing efficient and performant functionality. No matter how fast your code is, if your server itself isn’t running to it’s maximum performance, you’re not delivering your site as fast as you could be. By setting up Nginx and the new HHVM, you can get your server delivering fully loaded pages faster than most servers answer a request.

HHVM is an open-source virtual machine designed for executing programs written in Hackand PHP. HHVM uses a just-in-time (JIT) compilation approach to achieve superior performance while maintaining the development flexibility that PHP provides.

It is worth noting that HHVM isn’t 100% compatible with all of PHP, so if you’re using obscure functions, you may run into an issue. In all of my testing of WordPress, I never once had this problem, but it is something to keep in mind.

In this post, we’ll set up both Nginx and HHVM from scratch, and then install and setup WordPress. If you follow this guide, you can have an insanely fast server setup in less than an hour.

This post is written for specifically Ubuntu 12.04 LTS and 14.04 LTS. The steps outlined will probably work on other versions, and with some modifications most likely anywhere. I prefer Ubuntu, though. I’ll be going through this on a Ubuntu 14.04 LTS installation.

I’m taking these steps on a brand new Digital Ocean VPS droplet, but you can use the server of your choice.

We want to start with a brand new installation, and install everything we will need. Our first step is updating all of the built in software, and then installing some small useful tools we will want.

$ sudo apt-get update
$ sudo apt-get install -y unzip vim git-core curl wget build-essential

The ‘-y’ flag we’re using for ‘apt-get install’ will tell apt that we want to answer ‘Yes’ to every question about whether or not we want to install a package. Because we’re also running as root (sudo), we want to only install from trusted sources, like the ones that are already added to Ubuntu.

We now want to install Nginx. It’s also possible to run HHVM with Apache, but our goal is speed and performance so we want to use Nginx. Nginx is a reverse proxy server, as well as a normal web server, and also gives us nice load balancing and caching.

The entire goal of Nginx and HHVM is to speed up everything our webserver does to create our final HTML pages that WordPress generates.

Installing Nginx is actually super simple on a new Ubuntu installation. We tell ‘apt’, our handy package manager how to find Nginx, and then we can just do a ‘apt-get install’ like we did previously.

$ sudo add-apt-repository -y ppa:nginx/mainline

1

After we do this, we want to do another at-get update to fetch the Nginx package we just told ‘apt’ about. Now we’re ready to install Nginx.

$ sudo apt-get install -y nginx

That’s it! Nginx is installed. We’ll configure it in just a bit.

Now we’re ready to install HHVM. For Ubuntu 12.04 and 14.04, we can follow the official guide.

Ubuntu 12.04:
$ sudo add-apt-repository -y ppa:mapnik/boost
$ wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -
$ echo deb http://dl.hhvm.com/ubuntu precise main | sudo tee /etc/apt/sources.list.d/hhvm.list
$ sudo apt-get update
$ sudo apt-get install -y hhvm


Ubuntu 14.04:
$ wget -O - http://dl.hhvm.com/conf/hhvm.gpg.key | sudo apt-key add -
$ echo deb http://dl.hhvm.com/ubuntu trusty main | sudo tee /etc/apt/sources.list.d/hhvm.list
$ sudo apt-get update
$ sudo apt-get install -y hhvm

So far, everything goes pretty smoothly. The longest part is probably waiting for the server to pull in all of the packages for the ‘apt-get update’ every time.

After installing HHVM, we get a pretty handy message.

********************************************************************
* HHVM is installed. Here are some more things you might want to do:
* 
* Configure your webserver to use HHVM:
* $ sudo /usr/share/hhvm/install_fastcgi.sh
* $ sudo /etc/init.d/nginx restart
* $ sudo /etc/init.d/apache restart
* $ sudo /etc/init.d/hhvm restart
* 
* Start HHVM at boot:
* $ sudo update-rc.d hhvm defaults
* 
* Run command line scripts with HHVM:
* $ hhvm whatever.php
* 
* Use HHVM for /usr/bin/php even if you have php-cli installed:
* $ sudo /usr/bin/update-alternatives --install /usr/bin/php php /usr/bin/hhvm 60
********************************************************************

This is nice because it tells us how to do our next few steps.

First we’ll run the first of the commands they tell us about to resart Nginx and install FastCGI using HHVM.

$ sudo /usr/share/hhvm/install_fastcgi.sh

After this, we want to tell Ubuntu to run HHVM on system start.

$ sudo update-rc.d hhvm defaults

And then we’ll restart HHVM, just for good measure.

$ sudo service hhvm restart  

At this point, HHVM is currently running, and will run whenever our server is started up. We can verify that HHVM is installed by creating a PHP file and making sure that HHVM can run it.

$ touch test.php
$ echo " test.php
$ hhvm test.php

We should get a return of “HipHop”. If that’s what you see, we know that HHVM is working perfectly.

At this point, we can start setting up the configuration for a website, but before we do that, there’s another handy command that our HHVM installation results screen gave us to have HHVM “pretend” to be PHP over the command line.

$ sudo /usr/bin/update-alternatives --install /usr/bin/php php /usr/bin/hhvm 60

What this lets us do is use PHP command line tools like WPCLI, PHPUnit, Composer, and all of those things, just like if we were running PHP instead of HHVM.

Let’s test this real quick by doing:

$ php -v

You should get a result like this:

3

One really nice thing about HHVM and Nginx is there is very little configuration we need to do to start working. We don’t really have to do anything at all and it is already running. The default file location of our files is “/usr/share/nginx/html”, and there is already a simple HTML file there. I like to make an alias so that I can quickly get to this location.

$ echo  "www="cd /usr/share/nginx/html"" >> ~/.bashrc

This creates an alias so we can simply type “www” and change to our file directory.

At this point we want to install MySQL, so that we can actually use WordPress, and not just PHP files. Oddly, this portion has more configuration then anything we’ve done up to this point. After running the following command, we’ll get a password screen to set our root password.

$ sudo apt-get install mysql-server

4

Now we’re ready to install WordPress and set up Nginx to work with it.

We’re going to head to our Nginx HTML folder and grab a copy of wordpress and unzip it.

$ cd /usr/share/nginx/html
$ wget http://wordpress.org/latest.zip
$ unzip latest.zip

And we’ll also do a little housekeeping while we’re here.

$ mv wordpress/* .
$ rmdir wordpress
$ rm latest.zip
$ rm index.html

Now we’ll set up our MySQL database.

$ mysql -u root -pYOURPASSWORDHERE

5

Now we’re inside a MySQL prompt inside our SSH prompt inside our terminal program. Prompt-ception!

First we create a database, and then add a user, set that user’s password, give that user permissions, refresh MySQL privileges, and then exit out of our MySQL prompt. Make sure you write down this information, as you’ll need it for wp-config.php later.

$ create database databasename;
$ create user username@localhost;
$ set password for username@localhost = password("yourpasswordhere");
$ grant all privileges on databasename.* to username@localhost identified by 'yourpasswordhere';
$ flush privileges;
$ exit;

And now let’s create and edit wp-config.php. To edit the file, you can either SFTP in and edit this file locally, or use Nano or Vim on the server. We’ll use Vim.

$ cp wp-config-sample.php wp-config.php
$ vi wp-config.php 

For Vi, to start typing you want to hit ‘i’ to go into insert mode, and when you’re ready to save and quit, just hit ‘ESC’ and then ‘:wq’. It’s confusing, I know.

At this point, we’re ready to set up Nginx to be able to run WordPress. We’re going to edit /etc/nginx/sites-available/default.

$ sudo vi /etc/nginx/sites-available/default

We want to change two things in our configuration.

We want to change Nginx to also look for PHP files.

server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        root /usr/share/nginx/html;
        *index.php* index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;
        include hhvm.conf;

And we want to change how files are loaded.

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ /index.php?q=$uri&$args;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
        }

Your complete file should look like this:

# You may add here your
# server {
#    ...
# }
# statements for each of your virtual hosts to this file

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
#
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /usr/share/nginx/html;
    index.php index.html index.htm;

    # Make site accessible from http://localhost/
    server_name localhost;
    include hhvm.conf;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ /index.php?q=$uri&$args;
        # Uncomment to enable naxsi on this location
        # include /etc/nginx/naxsi.rules
    }

    # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests
    #location /RequestDenied {
    #    proxy_pass http://127.0.0.1:8080;    
    #}

    #error_page 404 /404.html;

    # redirect server error pages to the static page /50x.html
    #
    #error_page 500 502 503 504 /50x.html;
    #location = /50x.html {
    #    root /usr/share/nginx/html;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ .php$ {
    #    fastcgi_split_path_info ^(.+.php)(/.+)$;
    #    # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
    #
    #    # With php5-cgi alone:
    #    fastcgi_pass 127.0.0.1:9000;
    #    # With php5-fpm:
    #    fastcgi_pass unix:/var/run/php5-fpm.sock;
    #    fastcgi_index index.php;
    #    include fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /.ht {
    #    deny all;
    #}
}


# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
#    listen 8000;
#    listen somename:8080;
#    server_name somename alias another.alias;
#    root html;
#    index index.html index.htm;
#
#    location / {
#        try_files $uri $uri/ =404;
#    }
#}


# HTTPS server
#
#server {
#    listen 443;
#    server_name localhost;
#
#    root html;
#    index index.html index.htm;
#
#    ssl on;
#    ssl_certificate cert.pem;
#    ssl_certificate_key cert.key;
#
#    ssl_session_timeout 5m;
#
#    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
#    ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
#    ssl_prefer_server_ciphers on;
#
#    location / {
#        try_files $uri $uri/ =404;
#    }
#}

Now we need to restart Nginx, and start the WordPress installation.

$ sudo service nginx restart

At this point you can navigate in your browser to your site and you should be good to go!

 

load-times

Here’s a screenshot a stresstest of a site running default 2014, with the steps outlined above. As you can see, these results are amazing for a $5/month server.

Comments

28 thoughts on “Setting up WordPress + Nginx + HHVM For The Fastest Possible Load Times

  1. I’m confused about something in the mySQL section. Shouldn’t this

    $ mysql -u root -pYOURPASSWORDHERE

    be

    $ mysql -u root -p YOURPASSWORDHERE <— note the space?

    and when setting up the database, in this part, should the "databasename" be a string that the user has to change just like YOURPASSWORDHERE?

    $ create database databasename;

    1. Actually, it is:
      $ mysql -u root -pYOURPASSWORDHERE

      MySQL is a bit weird, because there isn’t a space for the -p flag.

      and yes, you are correct – you should choose a unique database name.

      1. Thanks Brad, sorry for the dumb questions! I’m new to all this.

        I’m assuming that “databasename” isn’t the only thing which should be changed or customized here:

        $ create database databasename;
        $ create user username@localhost;
        $ set password for username@localhost = password(“yourpasswordhere”);
        $ grant all privileges on databasename.* to username@localhost identified by ‘yourpasswordhere’;
        $ flush privileges;
        $ exit;

        My guess is username@localhost should also be changed. Is this the same as root@DOdropletname?
        (I’m using digitalocean droplets), or something else?

        thanks

      2. It is better practice to run just:

        $ mysql -u root -p

        (i.e., do not append the password to the end)

        Otherwise your root MySQL password will get logged to your BASH history.

  2. I’m stuck at this last part: We want to change two things in our configuration. We want to change Nginx to also look for PHP files.

    I have no idea what I’m supposed to do? The two code snippets shown below “We want to change Nginx to also look for PHP files.” and “And we want to change how files are loaded.” are already there. Am I supposed to click “i” again like the above part of the tutorial? why? then exist with :wq? how do I copy/paste the /etc/nginx/sites-available/default file that you’ve provided and be done with it? Sorry if this is a daft question, I feel rather daft right now 🙁

  3. when trying to install nginx using “sudo add-apt-repository -y ppa:nginx/mainline” I get this error:

    Cannot add PPA: ‘ppa:nginx/mainline’.
    Please check that the PPA name or format is correct.

    I googled and found these two lines:

    sudo apt-get install –reinstall ca-certificates
    sudo -E add-apt-repository ppa:linrunner/tlp

    neither worked. I still get the same error

      1. Same issue. Using …/development worked.

        Weird since I thought that nginx/development was the “old” way and nginx/mainline was the “new” way.

  4. There’s a type on line 25 of the final Nginx config. It should read index index.php index.html index.html. There’s also a similar type in the code box below “We want to change Nginx to also look for PHP files.”

    1. Chris – I was getting a 403 error message until I went in and adjusted line 25 of nginx config file as you suggested. After that it worked! Thank you very much!

      I’ve spent the better part of a week trying to install WP on AWS Unbuntu running Nginx. Utilizing this tutorial was by far the easiest and quickest install (and the only successful one too).

      The only hiccup I ran into was at:
      $ touch test.php
      $ echo ” test.php
      $ hhvm test.php

      I did not get the return of Hiccup… Just a new line indented with a “>”….

      1. Had the same issue as you both and Chrises index fix worked. Thanks for that! For the issue with “HipHop” only showed up for me after inserting php tags in the test.php file.

  5. Hello
    Thanks for this tutorial
    However i have a question
    Can you help me how to setup this for subdomain and to make each domain and subdomains install in different folder for security reason?

    I’ve tried the ‘common’ nginx way (modify the /etc/nginx/sites-available/subdomains and such) but it failed…

    Thank you in advance

  6. After i changed the
    root /usr/share/nginx/html;
    index.php index.html index.htm;
    nginx: [emerg] unknown directive “index.php”

    any ide why?

  7. followed it to the letter his guidance, which I think is magnificent, but when I reboot and nginx fails. I see that the failure occurs when first placed index.php

  8. Hello,

    Thanks for this tutorial, i have one question to ask,

    If i have 3 wordpress sites they all use this /etc/nginx/sites-available/default configuration?

  9. I “discovered” HHVM early in 2015 and had several high-capacity WP sites setup. I went ahead and switched one site to HHVM and setup php5-fpm fallback. The changes required less than 5 minutes of my time once I had documented them.

    I was already running many pages at 1.2s load-times average for typical pages and posts, 1.9s load-times for the home page (large slider graphics). After HHVM I noticed at worst 30% faster performance, though typically I get page loads of 0.5s and 1.25s respectively. That’s 60% better on typical content pages and 40% on my most dynamic pages. Wow!

    I was impressed enough to move all my WP sites to this technology and I’m not even bothering to look at PHP7 which isn’t effectively available yet on Debian 8 (main) anyway. I have rarely had any glitches that the early stages of HHVM seemed to have with nightly builds… 1 ever. I think that must be an issue of days past (2014).

    I recommend hhvm with proper precautions such as a fallback technique. When I ran Blitz.io on my site, it wouldn’t have performed as well without a fallback; and when I install my custom software platform which- when it compiles- it stresses the system for about 5 minutes with at least +30% load and heavy db traffic; I noticed repeated mysql errors in my software (dev version) caused hhvm to crash and I was really happy I had my fallback in place. I’m still doing some thorough and deep testing to understand it better but I wouldn’t trade it for the world. I know these aren’t official benchmarks but these are real-world stats tested on real-world websites with changes in code that required just 5 minutes of my time.

  10. Hi,

    Do you recommend doing anything special in terms of DNS? I’m no expert but I notice some other articles on performance optimization often make mention of DNS.

    Thanks!

  11. This worked a charm. I’m running this with PHP7 and some plugins had to be disabled due to the lack of compatibility, however the bulk of them are okay. Significant speed improvement to be fair!

Have a comment?

Your email address will not be published. Required fields are marked *

accessibilityadminaggregationanchorarrow-rightattach-iconbackupsblogbookmarksbuddypresscachingcalendarcaret-downcartunifiedcouponcrediblecredit-cardcustommigrationdesigndevecomfriendsgallerygoodgroupsgrowthhostingideasinternationalizationiphoneloyaltymailmaphealthmessagingArtboard 1migrationsmultiple-sourcesmultisitenewsnotificationsperformancephonepluginprofilesresearcharrowscalablescrapingsecuresecureseosharearrowarrowsourcestreamsupporttwitchunifiedupdatesvaultwebsitewordpress