Google guru Matt Cutts has recently reviewed the year at Google and focused quite a bit time on performance, noting that the speed of a web site may become a factor in search results ranking in the future.
So I took a look at our busiest website using Yahoo's YSlow and was dismayed - but not terribly surprised - to get a 'D' rating for one of the most complex pages. The result for the overall speed has probably got a lot to do with the number of complex SQL queries but that wasn't what was bothering YSlow. So I spent a fun afternoon making these fairly simple changes.
Where to start? Yahoo's "Best Practices for Speeding Up Your Web Site" starts with Minimize HTTP Requests so let's go with that.
1. Combining and Minifying CSS
Google has a great tool called Minify which sits on your server and gathers up your CSS files and serves then minified, compressed and with cache control headers. It's a PHP app which you just download and slap on your server. Minimal setup is required.
This site is ten years old and now has six CSS files, including two that were only for print. They aren't big but that's not the problem. The problem is that they make multiple HTTP calls and that slows everything down. A compliant web browser only asks for two things at a time so it will wait while each pair arrive. In tests, using www.webpagetest.org, I trimmed off about 0.3s (about 25%) of the time taken to start rendering the page. Why was I worried about the time taken to start rendering, rather than completion? Because that's what user's notice. Once the page has started to arrive, they feel rewarded. As long as the rest of the page arrives with out undue delay, yhey'll be happy.
The first thing I did was remove the need for the Print CSS files by moving their content into their Screen equivalents and wrapping it with @media print { }. The @media instruction didn't work in Netscape 4 and it looks like parts of this site date back to when Netscape 4 mattered. So then there were four.
The next step was to let Minify loose on the remaining four. By editing the groupsConfig.php file in Minify, you can define groups of CSS files. Version control is useful here as it will allow me to override the cache control I'm about to impose. So I set up groups:
'west-one-v01' => array( '//_css/westwing.css',
'//_css/firstfloor.css',
'//_css/nifty/niftyCorners.css',
'//_css/main.css' ),
Note my old friend Nifty is still in use (but probably not for much longer). I have called my group 'west-one' but have added 'v01' to the group name. If I need to override the cache control, I can create an identical group with a new name 'west-one-v02' and link to that instead.
Then I wrote a PHP function to generate the link:
<link type="text/css" rel="stylesheet" href="/min/g=west-one-v01" />
It's easy to change that function if I need to in the future - easier than changing a Dreamweaver page template.
2. Combining and Minifying Javascript
Minify will do the same job on Javascript. I don't really remember what half this Javascript does or I would feed it through Google's Closure compiler. That would hugely improve it but there's not much of it so I'm going to skip ahead.
As with the CSS files, I can create a Minify group that covers most of them. However, I also use Mint and I can't do anything about the link to its Javascript. Mint is/was a great package and well worth every cent of its $30 price but Google Analytics must be very hard for the excellent Shaun Inman to compete with. I'll keep using it because it's one-page summary of a website is still better than paging through GA.
So I've left Mint where it is. Maybe it could be moved from the header to the footer which would improve the time the page takes to start rendering but I'm not certain the gain is worth it. I'll ponder on that. I hope Shaun will to.
3. Cache Control
So far, so good. YSlow likes what I've done and now gives me Grade A on "Make fewer HTTP requests" but is still giving me an 'E' for Add Expires headers.
I'll do that it two ways. Firstly I can edit Minify's config.php and tell it to put 30 days on the CSS and Javascript. Actually yahoo suggests a month so I've made it 32 days to Yahoo gets off my back.
$min_serveOptions['maxAge'] = 2764800;
I also need to cache my images. For that I've gone to my .htaccess file and added:
<FilesMatch "\.(ico|jpg|jpeg|png|gif)$">
Header set Cache-Control "max-age=2764800, public, must-revalidate"
</FilesMatch>
Again, if I need to change an image I'll have to give it a new name.
Again, YSlow is impressed and gives me Grade C. Remember that I haven't touched Mint yet.
4. Compression
Why isn't all website content compressed? It's so easy to do and all modern browsers accept it but still the majority of websites don't compress. I have no idea why. Maybe they'd just never thought of it. Like me!
I'm using Apache 1.3 and I should really be using Apache 2. If I was using Apache 2 I could use mod_deflate and just add these lines to my .htaccess:
<IfModule mod_deflate.c>
# compress all text & html:
AddOutputFilterByType DEFLATE text/html text/plain text/xml
</IfModule>
But that doesn't do anything because I don't have mod_deflate. But I do have mod_gzip so this should work:
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</IfModule>
But it doesn't work because I'm hosting this at Fasthosts. I really like Fasthosts - I've used them for ten years and they are completely reliable. They aren't going to be the most up-to-date on technology but at £50 a month for a reseller account, you can't expect them to be. Anyway, GZIP didn't work and the engineer couldn't help.
But there is a third solution - to do it in PHP. Amazingly, all you have to do is add one line to the top of your PHP files:
<?php ob_start("ob_gzhandler"); ?>
But you do need to ask yourself whether this is worth it. Out of the 1.2 seconds it originally took my page to arrive, less than 0.05 seconds was taken up receiving the content. On pages with lots of content, this may be worthwhile but not everywhere. Also, if your page is designed to lay out one piece at a time, you may not want to buffer the output, which is what this command will do.And that's where I stopped. A page that started out taking 1.2 seconds, now takes less than 0.5 seconds. If I move Mint into the footer, I can trim that further. YSlow now gives me a Grade B for my smaller pages, where Mint is more significant, and a Grade A for my larger pages, where I've added compression. Not bad for two hours' work.