Whip-Snapping Fast WordPress with Nginx and HHVM-Part Number The Last

Silly Travolta.  You can't make blogs with your mind!
Silly Travolta. You can’t make blogs with your mind!
Phenomenal job! You’ve got HHVM spitting out PHP and Nginx spitting out…lots of other stuff, and WordPress is sitting there serving up your mind bullets to everyone who comes by.

Where are you going? You’re not done. There’s a few more steps before you are finished setting up your super duper fast WordPress blog and then you can post two or three posts before finally forgetting all about it and letting your blog languish in the internet backwaters for years until your host pulls the plug because you forgot you even had a server bill to pay. So much to look forward to!

We still have some more speed buttons to push, a little security tweaking, and some assorted little details.

Hit the Turbo Button

So we’ve done what we can to make sure that the PHP-ness of our blog doesn’t slow things down. But even better is to try and make sure it doesn’t have to hit up PHP at all. (Yes, I know, all that work for nothing) There are some nice WordPress plugins W3 TotalCache and WP SuperCache which, you guessed it, cache things. The idea is your server will spit out static content much more quickly and nimbly than when it has to interpret code. So our next step is to install one of these plugins. For the purposes of this tutorial, I’m going to assume W3 TotalCache because it is what I use and am accustomed to its ways, but there should not be many significant differences. After you install the TotalCache plugin, you will need to also make some additions to your nginx config.

First create an include file that you will use for the wp-totalcache config

sudo nano /etc/nginx/global/wordpress-w3-total-cache.conf

Paste in the following.

set $cache_uri $request_uri;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
        set $cache_uri 'null cache';
if ($query_string != "") {
        set $cache_uri 'null cache';

# Don't cache uris containing the following segments
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
        set $cache_uri 'null cache';

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
        set $cache_uri 'null cache';

# Mobile browsers section to server them non-cached version. COMMENTED by default as most modern wordpress themes including twenty-eleven are responsive. Uncomment config lines in this section if you want to use a plugin like WP-Touch
# if ($http_x_wap_profile) {
#        set $cache_uri 'null cache';

#if ($http_profile) {
#        set $cache_uri 'null cache';

#if ($http_user_agent ~* (2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800)) {
 #       set $cache_uri 'null cache';

#if ($http_user_agent ~* (w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-)) {
  #      set $cache_uri 'null cache';

# Use cached or actual file if they exists, otherwise pass request to WordPress
location / {
        try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args ;

Now you need to edit your site’s nginx config to include your TotalCache config.

sudo nano /etc/nginx/sites-available/sitename.com

server {
  listen 80;
  server_name www.sitename.com;
  rewrite ^ http://sitename.com/$1 permanent;

server {
  listen *:80;
  server_name sitename.com;
  location / {
    root /var/www/sitename.com/public;
    index index.php index.html index.htm;
    try_files $uri $uri/ /index.php?q=$uri&$args;

  include global/wordpress-w3-total-cache.conf;

  location ~ \.php$ {
    root /var/www/sitename.com/public;
    fastcgi_keep_conn on;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /var/www/sitename.com/public$fastcgi_script_name;
    include fastcgi_params;

Another great way to reduce your load time is to get your site’s images closer to your readers. A CDN (Content Delivery Network) is a great way to server up images and static files from multiple locations, and will automatically pick the closest server to pipe the content to the viewer. There are several options for CDNs, most of which cost money. But you’re using WordPress! And guess what? The great people of WordPress have a CDN that’s free if you use their JetPack plugin.

The JetPack plugin is an all-in-awesome plugin which also has several other handy features that you’ll want to take a look at after you’ve installed, such as stats and enhanced comment forms and social connectivity. But right now we’re mostly concerned with the speed. So install the plugin and activate the CDN. You will need to have a WordPress.com account created and will have to activate the plugin against your account. But once done, you’ll have access to turn on the CDN and all the other handy little features that you’ll be able to browse on the JetPack plugin’s dashboard.

Secure the Area

OK, first a few little basic security tweaks to keep your blog from becoming a playground for 13 year old script-kiddies. We’re going to set the file permissions on our blog folders and files. Open up your terminal and run the following:

sudo find /var/www/sitename.com/public/ -type d -exec chmod 755 {} \;
sudo find /var/www/sitename.com/public/ -type f -exec chmod 644 {} \;

Next we’re going to move the config file out of the root directory of our blog for just a little extra security, since it has some compromising information about our database. Don’t worry. WordPress will look in the current directory and one directory up by default.

sudo mv /var/www/sitename.com/public/wp-config.php /var/www/sitename.com/wp-config.php

Now another thing we want to worry about is someone trying to just guess our password with a brute force attack. There’s a few things we’ll do about that. First is to install an application on our server that will detect this sort of thing and block it. Basically if it detects any suspicious activity, it temporarily puts the offending IP Addresses in the Time Out Corner.

sudo apt-get install fail2ban

Just having this installed will help. But you can adjust some of the d-faults and such if you prefer. For example, you might want to put your personal ip address in the ignoreip line. (assuming your ip doesn’t change)

sudo nano /etc/fail2ban/jail.conf

You can also install some plugins into WordPress itself to help your security. An example plugin I have used is Wordfence Security. It’s a freemium plugin. The free version does have a lot to help you with making sure you’re secure. It uses attack information it’s collected from other installations of its plugin across the Interwebs to keep up to date information and help block distributed attacks. It lets you customize some of the blocking preferences and includes ‘Geo Blocking’ by location. It keeps an eye on DNS changes. And it keeps an eye on the source code of your site to make sure your WordPress install and most of your plugins have not been tampered with by attackers. (You will want to watch the first time you scan your source however, and make sure you ignore your change to the wp-includes/wp-db.php file that we made for HHVM.) There are a lot of other features that you can get with the premium upgrade that you may take a look at. But the free version has some nice built-in monitoring.

Another thing you can do to secure your login is to use two-factor authentication. There are several plugins to do so. One such handy plugin is the Google Authenticator plugin. This plugin alters your WordPress admin login to require an additional code that you generate using a downloadable mobile phone app that Google created. (Google did not create this plugin. They simply created the app that people can use to add the two-factor security to their web sites.)

Before we’re done with security, let’s not forget about SPAM. Make sure you activate the Akismet plugin that WordPress so kindly includes!

Keep on Cruising

We want to keep your groovy fast WordPress site running without downtime. Or prepare for disaster in case your web server tanks. So first things first, jump in your JetPack control panel and turn on site monitoring. This also awesome and free service from WordPress.com will monitor your site’s uptime and alert you by email if your site goes down.

Second, let’s back it up. The best backup is backup we don’t have to do ourselves, because we are humans and will forget because we are too busy watching cat videos online. With that in mind, let’s install another plugin. This one is Updraft Plus. Again, a freemium plugin with more features if you pay for premium access. This one will automatically roll up your WordPress database and configuration into a ball and back it up to FTP, email, or many different online storage providers such as Dropbox! Install. Configure. Forget.

Another handy little thing you can set up is an Upstart script for HHVM. This will ensure that if your server needs to reboot, HHVM will come back up when it restarts. Or if you need to manually stop or start your HHVM server, you’ll be able to do so.

sudo nano /etc/init/hhvm.conf

# hhvm - HipHop VM
# The HipHopVM server provides a high performance PHP stack and web server.

description     "HHVM server"
author "Yermo Lamers http://twitter.com/yermolamers"

start on filesystem or runlevel [2345]
stop on runlevel [!2345]

respawn limit 10 5
umask 022

exec hhvm --mode daemon -vServer.Type=fastcgi -vServer.Port=9000

Do not chmod this file to make it executable. It is a text file.

Now you’ll be able to run sudo start hhvm or sudo stop hhvm.

Commence WordPress-ing

Now, you have a super fast WordPress site that just won’t quit. Woohoo! All that’s left now is to go grab yourself a theme (itself probably its own post series), grab a beer to lubricate your thoughts and make sure your mind bullets come fast and free, (or, you know, a not-beer if you are too young or unable) and start writing!

7 thoughts on “Whip-Snapping Fast WordPress with Nginx and HHVM-Part Number The Last”

  1. Thank you! this series was really helpful. Would it be too much to ask to check if HHVM plays nice with buddypress? A real world benchmark comparing this stack with the typical LAMP stack would also be bitchin’


    1. Sorry for the late reply. I don’t use buddypress and some quick searching hasn’t turned up any specific examples from users. However I hear HHVM is officially supported by WordPress in 4x so once upgraded to that I would think you’d have a better shot of buddypress or anything that relies heavily on WordPress core.

    1. forgot to mention … in Ubuntu 14x (not sure on other flavors) the init file for HHVM mentioned in “Keep on Crusing” should be: sudo nano /etc/init/hhvm.conf instead of …/hhvm. I was able to use sudo start|stop hhvm after adjusting that.

      1. Thanks Matt. Good catch and I’ve updated the post! That would also have prevented automatic restarts if the system rebooted so important detail.

  2. Hello,

    This is very informative, about the cache, can i use the codes from my .htacess file? I use wp rocket. Also i might use your tutorial to set up my again.

Leave a Reply

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