See comments for important information about HTTP caching issues with URLs containing query string parameters.
When including CSS and JavaScript resources in your pages you should version file paths and update these version numbers every time the files change. This is necessary as a visitor's browsers may, depending on settings, continue to cache files even after a change. This can result in a mismatch between your HTML and these external resources which may cause rendering or functionality problems. One will likely encounter similar caching issues with images.
So if we work on the basis that we should version these resources and that changing version numbers forces the browser to reload those resources then we can actually gain large performance improvements by explicitly instructing the browser to cache them for an extended period of time (say 10 years) thereby limiting the number of times the browser goes back to check for fresh copies.
This can be achieved fairly easily within the virtual host settings of your Apache conf file. Simply add the following directives within the relevant virtual hosts section substituting /assets/ with the file path corresponding to the location where your CSS, JavaScript and images are stored.
<Location /assets/>ExpiresActive OnExpiresDefault "access plus 10 years"</Location>
If you have the mod_deflate module installed then you can gain an additional performance benefit by gzipping CSS and JavaScript resources to reduce the amount transmitted down the wire.
<Location /assets/>ExpiresActive OnExpiresDefault "access plus 10 years"SetOutputFilter DEFLATE</Location>
These directives can also be added to an appropriately placed .htaccess file but as Stuart explains there are performance implications with this method so it should only be chosen if you don't have access to your Apache conf files (perhaps because you're using shared hosting).
Applying both these settings covers off a couple of recommendations made by Yahoo!'s YSlow performance testing tool so not only will you end up with a faster site but you'll also improve your YSlow rating by a grade or two.
Of course manually versioning files is a pain, it's repetitive and as Stuart is always reminding me manually repetitive tasks should be eliminated wherever possible. In the past I wrote a script which automatically merges CSS or JavaScript files and versions the combined output. Here's some simpler code which versions individual files based their last modified date - every time the file is updated it's version number updates accordingly.
<?phpclass Version {private static $aLookup = array();public static function Get($sFilename) {if (!array_key_exists($sFilename, self::$aLookup)) {$sRealPath = realpath($_SERVER['DOCUMENT_ROOT'] . "/$sFilename");if (file_exists($sRealPath) && ($iTimestamp = filemtime($sRealPath))) {self::$aLookup[$sFilename] = $iTimestamp;$sFilename .= "?v=$iTimestamp";}} else {$sFilename .= '?v='.self::$aLookup[$sFilename];}return $sFilename;}public static function GetLink($sFilename) {return '<link rel="stylesheet" type="text/css" href="'.self::Get($sFilename).'">';}public static function GetScript($sFilename) {return '<script type="text/javascript" src="'.self::Get($sFilename).'"></script>';}public static function GetImage($sFilename, $iWidth, $iHeight, $sAlt = '') {return sprintf('<img src="%s" width="%d" height="%d" alt="%s">', self::Get($sFilename), $iWidth, $iHeight, $sAlt);}}?>
Download plain text version (Updated version to fix HTTP caching issues)
Using it is pretty simple - wrap the required function around the file path you'd normally have supplied to link, script or img tags.
<link rel="stylesheet" type="text/css" href="<?php echo Version::Get('/css/dark.css'); ?>">
In fact it's even easier than that - I've also provided some wrapper functions which make outputting link, script and img tags a little bit easier. The example above could be written more simply as follows:
<?php echo Version::GetLink('/css/dark.css'); ?>
Ahh, this is just what I was looking for, I looked at your script that merges and automatically versions css and js earlier, but I couldn't use that as I include different css for the various versions of IE (I don't like css hacks).
Thanks for sharing, I will definitely use this, and leave another comment about my experience...
I'm sure you can make it even shorter, memoization shouldn't be that verbose.
I'm just wondering how does realpath behave on Windows? http://uk.php.net/manual/en/function.realpath.php#72416
Your examples are using absolute path to css/js files. Is the '/' useful then?
Cheers,
-- Yoan
PS: very cool captcha.
As I just said over on Stuart's blog, I believe that file paths with query strings should not be cached as per the HTTP spec, so you're safer versioning the filename rather than the request path, so something like (untested):
and the following in your httpd.conf:
Which rewrites the above file name of "/static/file.1207433992.css" or similar to "/static/file.css" on your local box.
Note: Apache conf untested, but you get the idea. :)
Sorry, my mod_rewrite has fail:
is better.
Also my regex had a bit of optimisation fail:
Has one less capture (thanks Stuart for coming to my desk and teaching me the ways).
@Yoan - thanks for posting shortened code. It needs slight changes to prevent it writing v=-1 in situations where it's not able to read the file but other than that much improved.
I added the extra slash (/) because, although I've specified absolute paths others may not. Where realpath encounters 2 slashes next to each other it'll reduce to one so the additional one isn't a problem in the case where absolute paths are specified.
I'm not sure about realpath on Windows - certainly PHP on Windows has lots of issues so I wouldn't recommend using it unless one absolutely has to. Linux based hosting is usually cheaper anyway so unless one has to run alongside other Windows based systems, connect to SQL Server databases or you're developing on a Windows based development box this shouldn't be an issue.
@Brad - thanks for pointing this out. Umm, my function now has fail I guess. Given the problems you highlight it would definitely be better to throw in an additional rewrite rule to allow the version information to be written before the extension.
I've rolled Yoan's suggestions and code and yours into a new function:
Download amended class as plain text
The corresponding regular expression you'll need to add to your Apache conf (or .htaccess file) is:
Where "static" is the directory containing your resources.
For a Python version see Stuart's version of this code.
My comment is more in line with what Adriaan has commented on. that is my inclusion of "different css". I am not an expert but I also like to avoid hacks at all costs. I am going to give what you have here a shot and see what happens. Yoan great spot. I am working hard on getting to the level of expertise I see here but it seems there is some real talent above. I will be back with questions I am sure. Thank you for this!
An update since this was first posted and discussed:
Over on the YUI blog Yahoo! posted about their service for combining JS resources. For this service they do use URLs with query string components and as Eric explains in reply to my comment browsers will cache these URLs as long as expires headers are set.
In the post above these expires headers are set so it seems likely the original technique described here and Brad's proposed changes would both work fine.
Wow, I have had a lot of issues with browser that chache pages and especially with Javascript updates. Never came to my mind to create the file names with version numbers. And this automations comes even more in handy.
Thanks for saving me a lot of trouble.
Wow, I have had a lot of issues with browser that cache pages for my clients and especially with JavaScript updates. Never came to my mind to create the file names with version numbers. And this automation comes even more in handy.
Thanks for saving me a lot of trouble.
That's the way to go. Never get confused about finding the problem, files will always be the newest and will be loaded again.
chat | Porno izle | sex izle | sicak sohbet Dear Admin, I thank you for this informative article. And I thank you for this I follow your vendors. It’s verry good. I wish you continued success whould you like. sicak chat | sohbet | chat | sohbet siteleri | chat siteleri | sohbetim | sohbet siteleri | This is a great resource for growing your buisness.There are various aspects in buiness management and to grow the business.This is a very useful for tool for young entepreneurs. Thanks You Admin chat siteleri | sohbet odalari | chat odalari - sohbet - sohbet kanalları - chat kanalları - mynet sohbet | Chat Sitesi
# wasn't paying enough attention to them (a classic case of not RTFM). I wish all projects did it this well. # Being able to query models from the python command line shell. It also doubles as a fairly quick (and I guess scriptable) way to add test data to your database.
perfect thanks
good blog thanks admin