

Lately I've been diving into the world of website performance optimization. This article is the first entry in a series about website optimization.
A lot has been written on this subject already but I figured it would be great anyway to share my experiences with you all in order to consolidate what I've learned and because I may even come up with something new. You never know!
The site I'm using as a 'showcase example' for my optimization odyssey is Eurobands. I could have used my own blog but it's currently not in a very easy to maintain state and it's running a less widely known publishing system compared to the one Eurobands is using: Pivot. Eurobands is running WordPress which is used by a much larger audience which is why I figured this site would be a better candidate to dedicate my article to. My own site will get the royal treatment as soon as Pivot 2.0 comes out.
Two types of performance
There's two different ways to look at a website's performance. First of all there's raw server performance. How many page request can a server handle before it collapses under load and more important: how fast can it process them. On a high volume website this aspect of performance is very important because it determines how many users a machine can serve before additional server capacity is needed. This article is not about this kind of performance tuning. WordPress comes with a pretty robust caching engine built in which makes it perform as fast as it possibly can.
The second way to look at performance is what we could call 'perceived performance'. It's the speed at which your site appears to perform to the user. Even if your server is capable of serving all files required to render your website's pages in a visitor's browser as fast as it gets, your site may still appear slow to the user. A fast pipe doesn't necessarily result in a fast website. Slow client-side performance can have a plethora of causes. There's an awful lot to gain on the front end side of the game which is what this article is about.
Reducing graphic file sizes
Graphics account for most of any web page's total download size. And as we all know, downloading content takes time and therefore affects perceived performance. Of course the most effective way to speed things up when it comes to download sizes is to reduce or even eliminate the use of graphics. However, we do want our website to be easy on the eyes so graphics are going to be used in most websites anyway. What we CAN do is make sure the graphics we use are as small as possible. You can experiment with JPEG compression in Photoshop and for PNG's, which are used quite a lot on Eurobands I've used PNGOUT which does an excellent job at reducing PNG graphics to an absolute minimum file size.
Reducing the amount of images
Making your images as small as possible in size is important for your website's performance. However even with a fairly small total amount of kb's for all your images there's another aspect that may negatively affect the performance: The amount of images. As most of you know, every separate image on a website results in it's own HTTP request to the webserver. Initiating server requests takes time. Therefore downloading 10 images of 1kb each will take considerably longer than one image of 10kb even though we're downloading the same amount of data.
Many websites use collections of icons or menus that consist of many small images. These are a perfect target for optimization. On Eurobands I placed all images for the menu and all images for the frontpage image replacement headers in two single files. Have a look at this one for the menu and this one for the frontpage headers. By doing this I reduced a whopping 25 HTTP requests for images to only two! A remarkable performance win. The mechanism behind this is called CSS Sprites. If you want to learn more about how to use this technique, read Dave Shea's excellent article on A List Apart: CSS Sprites: Image Slicing�s Kiss of Death. A great example of a high volume website using this technique in a very effective way is Yahoo!. There's a huge CSS sprite containing all of their menu icons. Check it out here
Forcing the browser to perform more simultaneous server requests
Most web browsers only handle two simultaneous HTTP requests to one host at any given point in time. This can be overridden by hacking the registry for MSIE or tweaking network.http.max-persistent-connections-per-server in Firefox' about:config page. Needless to say, 99,9% of your visitors won't be geeky enough to actually do that. Therefore we'll have to trick Joe User's browser into performing more simultaneous requests.
Of course most websites consist of more than just two files which means that all consequetive requests will be placed on a stack to be handled on a 'two at a time' basis. After reading Steve Saunders excellent article Maximizing Parallel Downloads in the Carpool Lane on the YUI blog I went ahead and created three extra subdomains for Eurobands: images1.eurobands.us, images2.eurobands.us and images3.eurobands.us. After this I tried to evenly distribute image requests in my CSS files over these three domains and the www.eurobands.us domain. This resulted into more simultaneous requests and therefore: more perceived speed on the client!
important note: If you want to try this technique youself, DON'T try to be smart and randomize hostnames for images on a per-request basis or you'll run the risk of the same image(s) being loaded from different servers instead of being cached by the browser which of course results in an actual performance hit instead of a gain. Make sure each file is downloaded from the same domain at every request.
Another word of caution is about the amount of different hostnames you create. When using too many hostnames it will increase the amount of simultaneous HTTP requests but it will also increase the amount of DNS requests which in it's turn affects performance negatively. Steve mentions a healthy balance between an optimal amount of parallel HTTP requests and required DNS requests to be performed of 2 to 4 hostnames. It's not recommended to go higher than that or you may loose speed more than you gain.
Of course this technique can be used not only on images but also on CSS files and Javascript files. However there's more to be optimized when it comes to those. Read on!
<script> tags in your header? Move them to the bottom!
Here's another interesting fact I didn't know about until recently: Whenever a browser encounters javascript, it halts the stack of yet-to-be-performed HTTP requests until the script has finished parsing / executing. A lot of websites feature numerous bloated Javascripts in their <head> section, needlessly slowing down client side performance. For an optimal perceived performance from a visitors' perspective we want to make sure the page starts rendering as soon as humanly possible, even when not all images haven't loaded yet. In order for this to happen we need to make sure that the CSS gets loaded as EARLY as possible and the Javascript as LATE as possible. Our mission therefore is: move all <script> tags to the very bottom of the page instead of the <head> section where they usually reside. Especially when you're using fat javascript frameworks such as Prototype.js, YUI, JQuery and additional code that performs all kinds of fancy DOM manipulation you'll see that moving them all to the bottom of your page will make the page start rendering earlier and therefore appear speedier to the user. On Eurobands quite some bulky javascript is used. Moving it all down made a world of difference to the speed of the site.
Even less HTTP requests: merging CSS and Javascript
As mentioned before, lessening the amount of HTTP requests required for a webpage dramatically speeds things up. I had already cut down on the amount of images being pulled from the webserver by using CSS Sprites but there was still another area in which improvements were possible: CSS and javascript.
Eurobands uses quite a lot of WordPress plugins. Many of them insert their own CSS into the page which leads to more HTTP requests and a less speedy experience for the user. It's not easy to merge these together if we have to do it manually and after every plugin update we install I would have been forced to update my nicely merged CSS. Being the lazy fart that I am I was looking for something better. Eurobands used to have five CSS files which were included by various plugins. Of course I wanted to reduce this amount to just one.
WP-CSS-Streamliner: a new WordPress plugin is born
After some research I couldn't find any viable automated way to merge all my CSS. Therefore I decided to write a plugin that does all this for me without having to perform any maintenance. Enter WP-CSS-Streamliner! This new plugin does the following things:
- Search the page for included CSS embedded in <link> tags in the page and strip them out
- Fetch all these files locally and merge them into one CSS file in the original order in which the tags were found
- Remove all comments and unnecessary whitespace from the merged CSS
- Cache the created highly optimized CSS
- Insert a new <link> tag in the page <head> section to load the optimized cached CSS file
While it's doing all these things it tries to be pretty smart at it as well. If we'd just grab anything found in <link> tags and merge it together, serious breakage could occur. Therefore the plugin comes with a (simple) exclusion mechanism to exclude CSS we don't want to be processed. Two relevant examples are print stylesheets or IE only stylesheets within conditional comments. If these would be included in the merging/optimization process it would break the page rendering. Another problem I ran into was the fact that CSS files often reside at different places on the server and are likely to contain relative paths to images which would surely result into 404's on image request when they're all merged into one file served from one particular location on the server. Therefore the plugin intelligently tries to rewrite relative image paths found in CSS files into full URLs to make sure all images still get loaded when using the merged and optimized CSS file. Finally I needed a mechanism to make sure the cache gets expired as soon as any of the CSS files used on the site gets updated. Therefore the plugin checks modification dates on every CSS file and compares them to the creation date of the cached file. When any of the CSS files turns out to be newer than the cached file it automatically gets regenerated.
Adding GZip compression
Addition to the article: Several people on the 9rules forum wondered why I wasn't using gzip compression as well. Initially I figured people could enable that site wide for themselves until I realized that not everyone may be able to do that. Therefore I've added gzip compression to the plugin. The cached CSS is now sent to the browser through a small PHP file which sends the appropriate headers to make sure browsers cache it and compresses the CSS with gzip. The result is spectacular!
-
<?php
-
/* anti-asshole spray */
-
if(!preg_match('/(^[a-zA-Z0-9]+).css$/', $_GET['f'])) {
-
die('nope.');
-
}
-
ob_start("ob_gzhandler");
-
header("Content-Type: text/css; charset: UTF-8");
-
header("Cache-Control: must-revalidate");
-
$offset = 60 * 60 ;
-
$ExpStr = "Expires: " .
-
gmdate("D, d M Y H:i:s",
-
time() + $offset) . " GMT";
-
header($ExpStr);
-
if(!file_exists('../../'.$_GET['f'])) {
-
die('file does not exist.');
-
}
-
$sText = file_get_contents('../../'.$_GET['f']);
-
$sHash = md5($sText);
-
header("Last-Modified: ".date("D, d M Y H:i:s T", filemtime('../../'.$_GET['f'])));
-
header("ETag: "{$sHash}"");
-
readfile('../../'.$_GET['f']);
-
ob_flush();
-
?>
Code for css.php. To see the whole plugin's source code, click here.
Try WP-CSS-Streamliner
WP-CSS-Streamliner did some magic for Eurobands. It reduced the total amount of HTTP requests for CSS from 5 to only one and in addition to that it decreased the total filesize for CSS from 28kb to 5kb. A very nice gain without losing any flexibility and without adding any maintenance to the site.
view the source of the plugin
I'm not sure how 'bullet proof' this plugin is yet as it's VERY new but I've tested it on two other WP sites besides Eurobands and it worked like a charm. If you want to try it on your own WP blog you can download it at the bottom of this post. Make sure you read the README.txt that comes with it before posting a comment about it and/or emailing me about problems you may encounter using it.
Closing notes and open ends
A burning question you may have is: "Why didn't he include actual numbers to show the actual increase in performance after doing all these things?". The reason for this is quite simple and honestly a bit sad. I tried to gather data but concluded that my numbers wouldn't make sense. The reason for this is the hugely varying performance I seem to get lately from Dreamhost. The numbers just wouldn't have any meaning because there's too much variation in hosting performance going on for me. What I can say however is that on average the website feels an amazing lost faster than it did when I first released it without any optimizations. Take my word for it, these measures work.
There's one area I haven't touched yet which is the Javascript. Javascript can be merged and compressed in a similar fashion like the WP-CSS-Streamliner plugin does for CSS files. I will create a plugin for Javascript files soon and make a new post about it when it's ready.
Of course there's also HTTP Compression which can further trim down file sizes on your server. And there's probably even more things that I missed in this article. Feel free to mention them in your comments to this article.
All your remarks, comments, suggestions and feedback on the WordPress plugin are more than welcome!
Notes
These were added later and I'll continue to update them as new tidbits come up.
- The plugin now has it's own 'homepage' post.
- When moving the Javascript down you'll see your site doesn't get faster in an 'absolute' sense compared to what it was when it was still in your <head> section. However the perceived speed will increase greatly. When analyzing Eurobands with the FireBug plugin for Firefox you'll see that the whole page basically renders in seconds while the JS takes several extra seconds to completely finish doing it's stuff.
- Not all JS can be moved to the very bottom of the page. The script needs to be written as proper progressive enhancement for this technique to work. Therefore, depending on what kind of scripts your using you might need to keep one or more where you found them.
-
To place JS inserted by WP plugins at the bottom of the page you may need to hack the actual plugin. Find a line that says:
add_action('wp_head', 'some_plugin_function')
and change it intoadd_action('wp_footer', 'some_plugin_function')
. In case of dependencies when it comes to the order in which the scripts are inserted you may or may not need to add a third parameter to enforce proper ordering like:add_action('wp_footer', 'some_plugin_function', priority)
where priority is a number. The lower the number the higher the priority. - If you're using the WordPress plugin and you find your CSS to be broken after changing one of the files on a live server, have a look at wp-content/cached.css. If it contains PHP errors about being unable to open a file this is caused by one or more of the files being updated exactly when the plugin hit. It happened to me when updating CSS through FTP. If it happens just delete wp-content/cached.css and it will work (again). I'll try to update the plugin with a workaround for this problem soon. update: done!