Because we’re always on the look out for ways to speed up our web application, one of my favorite tools for optimization is the YSlow Firefox extension. Based on rules created by research done by Yahoo engineer, Steve Souders (his book High Performance Web Sites is a must read for anyone interested in front end engineering), the tool hooks into Firebug and helps you diagnose issues that can shave seconds off your pages’ load times. While we were able to implement most of the suggestions fairly easily, Rule #3, which specifies adding a far futures Expires header required a bit of elbow grease that some of you might be interested in.

Rule #3 recommends that you use set an Expires header on your static files (images, CSS and JavaScript) very far into the future (like 10 years) so that your browser’s cache is used to load those elements rather than making another HTTP request, which is costly when it comes to page load times. Implementing this is pretty easy. In your .htaccess file, you can use the following code:

#Far Future Expires Header <FilesMatch "\.(gif|png|jpg|js|css|swf)$"> ExpiresActive On ExpiresDefault "access plus 10 years" </FilesMatch>

However, Steve makes a little note about using this technique:

Keep in mind, if you use a far future Expires header you have to change the component’s filename whenever the component changes. At Yahoo! we often make this step part of the build process: a version number is embedded in the component’s filename, for example, yahoo_2.0.6.js.

We, of course, didn’t have a built in build process that added the version number to our static files. Obviously, we weren’t interested in changing version numbers by hand or having tons of different versioned files lying around in our SVN depository. And so motivated by a goal (increasing our Y Slow score) and sloth (not doing something manually), we figured out the following automated solution.

The first thing we did was set up some mod rewrite rules to allow version numbers in our file names. In our .htaccess file, we added the following lines:

#Rules for Versioned Static Files RewriteRule ^(scripts|css)/(.+)\.(.+)\.(js|css)$ $1/$2.$4 [L]

What this does is quietly redirects any files located in our \scripts\ or \css\ folders with version numbers in between the file name and the extension back to just the filename and extension. For example, I could now rewrite the url /css/structure.css as /css/structure.1234.css and Apache would see those as the exact same files. We only do versioned files for our JavaScript and CSS, but you could easily adapt the rule for images as well, like so:

#Rules for Versioned Static Files RewriteRule ^(scripts|css|images)/(.+)\.(.+)\.(js|css|jpg|gif|png)$ $1/$2.$4 [L]

Once that was in place, we wrote a tiny PHP function that would look at the last modified date of the file and automatically rewrite the url with that unix timestamp as the version number. Here’s that PHP function:

<?php function autoVer($url){ $path = pathinfo($url); $ver = '.'.filemtime($_SERVER['DOCUMENT_ROOT'].$url).'.'; echo $path['dirname'].'/'.str_replace('.', $ver, $path['basename']); } ?>

Then, in our PHP documents we would include the function and then call it like so in the HTML markup:

include($_SERVER['DOCUMENT_ROOT'].'/path/to/autoVer.php');<link rel="stylesheet" href="<?php autoVer('/css/structure.css'); ?>" type="text/css" /> <script type="text/javascript" src="<?php autoVer('/scripts/prototype.js'); ?>"></script>

When the pages load, our script would request the file modified timestamp and insert them in like this:

<link rel="stylesheet" href="/css/structure.1194900443.css" type="text/css" /> <script type="text/javascript" src="/scripts/prototype.1197993206.js"></script>

It’s a great little system and required very little effort on our end and resulted in a noticeably faster browsing experience for our clients that frequented certain pages often, because their browsers were taking full advantage of their primed caches rather than calling our servers every time they loaded a page. The best part is that when we make a change to a CSS or JavaScript file, we don’t have to worry about tracking or managing version numbers or multiple files.