By: Chuka Okoye Posted on: 2014-06-28
I don’t mean to badmouth WordPress when I say this, but if you deploy a WordPress based site without optimizing your install you’re just asking for a slow site.
Below is everything I know about optimizing a WordPress installation. It is by no means a list of everything you can do to optimize a server for WordPress, but its a damn good base. Because of the nature of tweaking servers, you’ll need access to the server config files (and most likely access to the root user on the server) to make most of these changes.
Update 2014-04-01: To be honest, this is mostly an article on optimizing your LAMP stack with some WordPress items at the end. I wrote it because I had seen some people put up some WordPress based sites that were struggling under load. It really applies to any general website deployment.
Update 2014-06-21: Updated with a snippet for excluding subversion directories via the .htaccess file.
Apache is the backbone of your server install. Getting it serving your content well is one of the more important parts of the optimization. Some folks will evangelize the removal of modules that you don’t need to reduce Apache’s memory footprint but I’ve found that this mostly creates issues in one form or another. Deleting this or deleting that disables functionality that you may not realize that you needed or wanted. I’ve found that those are best of being left alone. The default module set for PHP is long, but not terribly gratuitous.
Use mod_expires to Control Browser Caching
The mod_expires Apache module controls how the Expires cache control header is sent to the browser. Using this module can allow for aggressive caching policies so that user’s browsers hold on to the data longer.
For Example, in your VirtualHost config you can enter:
If you don’t have access to your Virtual Host config then these can also be used in a .htaccess file.
This is very aggressive caching, but by using WordPress’ version parameter when enqueueing your scripts and styles you can ensure that your clients are always getting the most current site resources. Increment your style’s or script’s version each time you push an update to your site and the browser will see it as a unique url and request the new data from the server. This way you can have your cake and eat it too – long cache times and a way to force the content to be re-downloaded when changed.
Use mod_deflate to GZip content
If you have mod_deflate active in your Apache install you can gzip the content that is delivered to browsers that understand gzipped content. This can decrease the amount of time it takes to transmit data to the user.
Again, we’ll need to add config parameters:
Disable the Etag Header
When using the mod_expires changes above the ETag becomes obsolete. Removing the ETag disables the browser’s ability to validate the file’s cache time so that they rely on the headers that are custom configured. This can save a little bit of overhead in Apache.
For these you’ll unfortunately need access to the Apache config as they need to be set globally. If you’re on a shared host, talk to your web host about it and see if they can accommodate you. On its own this is not a monster change, so if you can’t get this one done I wouldn’t sweat it. Every little bit helps, but this isn’t crucial.
mod_rewrite to exclude non-wordpress resources
When using “Pretty Permalinks” WordPress will try to catch any file request that wasn’t found on the filesystem. By default all requests that can’t be met by a file on the system are funneled to WordPress. If you serve a lot of auxiliary files for such things as bulk emails or web galleries this can incur a lot of overhead if a few files somehow go missing or a typo is missed in a url. One or two missing images in an email blast that in turn invokes 404 responses from WordPress can bring even a well endowed server to its knees. Fortunately its very easy to exclude these high-traffic directories from having an impact on WordPress.
Add a line like this to the WordPress .htaccess rules
so that you look like this:
If you use Subversion to manage and deploy your site then you’ll also want to deny access to the .svn directories. Add this line after the RewriteBase directive to keep people from looking at your subversion info:
There are a few different ways to accomplish this task, but this one is the easiest if you don’t control the server.
And, while you’re at it, exclude WordPress from handling the favicon too!
The PHP tweaks all require access to your PHP.ini file. I’ve not tried modifying these at runtime via the .htaccess file or though a ini_set() directive in code as these mostly benefit from being known early by PHP.
Alternative PHP Cache (APC)
PHP has a caching structure called Alternative PHP Cache, or APC, which can cache your compiled code. This means that instead of reading files from disk every time they’re needed PHP will dedicate some internal memory to caching your code. Less filesystem hits mean better performance, so this one can be good.
If you’ve got control over the server or your hosting package includes PHP’s APC and you have php.ini access then you should consider configuring it for action. Be careful, though, this one can make bugs tricky to diagnose if you forget that you’ve turned it on. If you’re trying to debug something on a live server then be sure to turn this off while you’re debugging.
Start with adding a block like this to your php.ini:
That’s the base amount of config that APC needs. If you really want to dig in and tweak then reference the PHP Manual for APC configuration.
Cache File Paths
PHP has an internal cache called realpath_cache that can hold on to file paths when they’re determined. Using this cache means that PHP can skip the file path verification step and jump directly in to grabbing the content when it can. Its a small win, but piled on top of each other small wins give great results.
To enable simply uncomment the two lines in your php.ini file that begin with
This doesn’t need to be set too high. 64 or 128K is plenty. If you’ve got a lot of plugins or a file heavy framework that you’re building on then lean towards the 128k setting.
MySQL tweaks rely on being set in the my.cnf file. So if you don’t have access to your MySQL config then don’t worry about this.
Enabling the MySQL Query Cache tells the server to cache the SQL query and its response so that common queries don’t have to be repeated. The MySQL server also monitors the database tables so that if any table used in a query has changed since the last query the cache is flushed and the query is performed anew.
To enable query caching edit your my.cnf and add these lines to the
Query cache sizes should be a multiple of 1024. MySQL will round down to the nearest multiple of needed. If needed for special purposes the Query Cache can be circumvented by beginning your queries with
WP Super Cache
If you run a high volume WordPress site you need to be running WP Super Cache on your production server. End of story. Setup can be a bit tedious, but the returns are indispensable. I’m not going to go in to the details of the plugin here. Others have written a lot about this and, frankly, the install docs pretty much give you everything you need to know.
If you’re more adventurous and have memcache installed on your server you can also check out BatCache as an alternative.
The WordPress wp-config.php file is a very under-used file. The options that can be set here can reduce a few trips to the database and allow WordPress to spool up a little bit quicker when responding to a request. As part of my work on the WordPress TextMate Bundle I’ve compiled a functionally complete wp-config.php file that can be viewed here. All the different options are documented as they are in the WordPress Codex.
The entries that I recommend using are:
If you’re a developer and have access or influence over the server config you can also put a few more tricks in to play.
Object Caching with Memcache
WordPress includes an internal object caching system which can store the results of database queries and, well, pretty much anything that you can shove in there. The downside to this cache is that without some kind of persistent data store it is only good on a per-page load basis. While this in and of itself is good, its not as good as it could be. Fortunately the WordPress developers realized its potential when building it and created the necessary hooks and replacement mechanisms to replace the simple built in cache with a more persistent solution.
Memcache and the Memcached Plugin for WordPress is the perfect solution for this. By implementing this solution the WordPress object cache is put in to the Memcache system instead of in to PHP’s page load only memory so that the pulled data can persist across many page loads. This can significantly reduce the amount of calls to the database and allow you to store pre-compiled stacks of data for later use. On this site alone I use this system to store at least 9 sections of pre-compiled HTML for output on top of the normal cache that’s performed by WordPress.
Consolidating & Minimizing Resources
If you’re like the rest of us, over time the addition of features and tweaks to layout produce more and more resource files to be included. This slows down site performance. Use a system like Minify to consolidate and compress your site resources and reduce both the number and size of the files that you need to deliver to the browser.
More information on this is available here.
Use a Content Delivery Network
If you can, offload resources on the site to other servers. Possible tweaks include:
So. That’s what I’ve got, and I didn’t even touch on what you should be doing to your theme to make it more efficient. Hopefully this gets your site on its way to being a fast loading dream for your users and keeps you away from those frustrating long load times.