There are plenty of WordPress plugins out there that promise to improve your page speed in various ways, but the truth is you can do a great deal without resorting to them and just making a few simple but effective changes to your Apache configuration. In fact, this article applies equally to any Apache website, not just WordPress.
As we know, keeping our page load times nice and snappy is the key to happy visitors and one of the important factors in SEO. Whilst it’s something to take seriously, it’s not too hard to understand with some pointers. This post will take you though the easy wins that are often overlooked when setting up a new WordPress site:
We’ll be using Google Page Speed and Yahoo’s YSlow browser plugins as a way of measuring the improvements in our page speed and getting tips for where to focus our efforts.
Leverage Browser Caching
cache-control header, which specifies the number of seconds the browser should keep hold of the file before it should ask for a new copy.
If you open up Firebug and switch to the “Net” tab, or the alternative if you’re using developer tools in another browser, you should see a list of all the resources your page is sending to the browser (you might need to refresh the page to get this).
Without browser caching we can see that all the page's resources are being downloaded from the server
The “Status” column shows us that all our page’s resources are being delivered by the server – this is what the “200 OK” status means. We want to change this so that the browser gets the resources from its cache, which is far quicker.
To do this we will use the Apache
mod_expires module, which will allow us to define how long the server will tell the browser to cache the various types of resource.
First you’ll need to endure that
mod_expires is enabled on your server, so open up your Apache
httpd.conf file and search for the line
LoadModule expires_module modules/mod_expires.so it’s commented-out by default so uncomment it or add it in as necessary.
Next, add in the configuration. Just like for compression, above, this can be added to the http.conf or .htaccess files.
For our site, we use the following configuration:
# Content expiration headers
ExpiresByType text/html "access"
ExpiresByType text/xml "access"
ExpiresByType application/xml "access"
ExpiresByType application/xhtml+xml "access"
ExpiresByType text/css "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType application/pdf "access plus 1 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
ExpiresByType image/ico "access plus 1 year"
ExpiresByType image/icon "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# Need to register the icon mime type
AddType image/vnd.microsoft.icon .ico
ExpiresByType image/vnd.microsoft.icon "access plus 1 year"
Let’s break this down:
ExpiresActive On switches on cache-control for all our resources.
ExpiresDefault "access" sets the default cache expiry to be instant – as soon as the content is accessed.
- The following lines are all similar to
ExpiresByType text/css "access plus 1 month" where we say what type of resource we are talking about (in this case CSS files) and how long they should be cached (in this case 1 month after they are accessed).
- Note that the HTML and XML types are all set to “access”, this means they won’t be cached by the browser. We chose to do this so the user is guaranteed to see the very latest content, but what you choose will depend on your site.
- The final couple of lines add in a non-standard type of
image/vnd.microsoft.icon, which is used by favicons, and then adds an expiry for that type.
After making this change, restart your server and take another look at your page. The first time you look at it, it will still request all the resources from the server (as it hasn’t been told to cache them yet) but when you look at the headers of one of your files you’ll see the new cache-control header is in there.
Note the "Cache-Control" header is now present
The next time you refresh your page, your browser will know to get these resources from its cache rather than request them from the server, so your new page load should look more like the following:
With browser caching enabled we can see that all the page's resources are being fetched from the browser's cache
Much better! Now all our content is coming straight from the browser cache (denoted by the “304 Not Modified” statuses) and you can see the loading speeds are all significantly faster.
When the server sends information to the browser it can be configured to compress the data in order to make the loading speeds quicker. To enable compression in Apache, first you will need to check that
mod_headers are enabled on your server, so open up your Apache
httpd.conf file and search for the following lines, they are commented-out by default so uncomment them or add them in as necessary:
LoadModule deflate_module modules/mod_deflate.so
LoadModule headers_module modules/mod_headers.so
Next, add in the configuration. This can be added to any of the following locations:
- The base-level of the httpd.conf file so that it applies to all your server’s sites.
- Into one of the
<location> sections so that it applies to just that location’s files.
- Into a .htaccess file so that it applies to just the files governed by that file.
For our site, we use the following configuration:
# The list of mime types we want to compress
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom_xml
# Netscape 4.x has some problems...
BrowserMatch ^Mozilla/4 gzip-only-text/html
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0 no-gzip
# MSIE masquerades as Netscape, but it is fine
# BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
Let’s break this down:
- There are a number of lines similar to
AddOutputFilterByType DEFLATE text/html – these lines tell the server to compress specific types of file, in this example, HTML files. Here we list all the types of file we want to compress and anything we leave out will be sent normally. Note that images and PDFs are not included, it’s only text-based files.
DeflateCompressionLevel 9 sets the compression level to its highest. This will take more processing power so adjust this according to your needs.
- There are a some lines that begin with
BrowserMatch, these take care of compatability with some older browsers.
- Finally there is a section that ensures proxies work well with our compression.
Now that’s all set up Apache will compress all the content it can and, using Firebug, you’ll see both the size and load time of all the text-based resources drop nicely.
A common complaint of YSlow is that “There are x components with misconfigured ETags” – because we have taken control of our browser caching, we don’t really need ETags any more (they are just another way for your browser to control its cache) so the simplest solution is to turn them off.
To do this you’ll need to have
mod_headers enabled (as for enabling compression, above) and add the following to your httpd.conf or .htaccess file:
# Turn off ETags as we're manually managing our caching
Header unset Etag
This removes the
Etag HTTP header and YSlow will stop complaining and give you an A-grade for ETags.