javascript cache problem, solved

Almost every time someone says to me that something I was working on is broken, the answer is “clear your cache”, and that automagically fixes everything. However, that’s not an ideal solution – ideally, the problem would never happen in the first place.

So, why does the problem happen?

Let’s say that there is a HTML file which calls a JS function like this: showImage(); – the JS function is included from the external file /j/images.js.

Browsers are usually set to cache .js files, and that’s the correct behaviour for the most part. Unfortunately, when a file needs to be fixed, it can cause problems.

For example, let’s say that I’ve corrected the function name to match my usual naming scheme – images_show();. I change the reference in both places. The browser reads the new HTML file from the net, but loads the JavaScript from the cache – suddenly there’s a mismatch which causes a problem.

So, how to get around this?

The solution I’m using at the moment involves a little bit of mod_rewrite and PHP.

Sticking with the contrived example, let’s rewrite /j/images.js so it is accessible from /j/images (using /.htaccess):

ExpiresActive On
ExpiresDefault A259200
RewriteEngine on
RewriteRule ^j/images$ /j/images.js [L]

Now, we add a little magic. We want to change the URL if the file has changed. The only way to know this is to look at the modified date of the file.

In your PHP, you could do it like this:

<script type="text/javascript" src="/j/images/<php? echo md5(`ls -l j/images.js`); ?>"></script>

and then change the .htaccess file to allow that:

ExpiresActive On
ExpiresDefault A259200
RewriteEngine on
RewriteRule ^j/images/(.*)$ /j/images.js [L]

Now, if no file changes happen, then the MD5 hash (and therefore the URL) will be cacheable, and if the file changes, then the URL will automatically change as well.

…and that’s not all!

I like to aggregate my JavaScript files to reduce the network pain felt by the browser. In my CMS, it’s done with a /j/js.php file. Here’s a short excerpt:

<?php
$js=file_get_contents('jquery-1.2.3.min.js');
$js.=file_get_contents('js.js');
$js.=file_get_contents('tabs.js');
$js.=file_get_contents('addrow.js');
$js.=file_get_contents('formhide.js');
/* more files */

header('Cache-Control: max-age=2592000');
header('Expires-Active: On');
header('Expires: Fri, 1 Jan 2500 01:01:01 GMT');
header('Pragma:');
header('Content-type: text/javascript; charset=utf-8');

echo $js;

That’s then pointed to with this line in my .htaccess:

RewriteRule ^js/(.*)$ /j/js.php [L]

And it’s referenced in the browser like this:

echo '<script type="text/javascript" src="/js/'.md5(`ls -l j`).'"></script>';

Simple, innit! That simple trick now keeps track of a number of files, and the browser knows immediately if there are any changes.

BTW: The same trick can be used with images, css, and any number of other “static” objects.

11 Thoughts on “javascript cache problem, solved

  1. Mikul Gohil on May 7, 2008 at 7:30 pm said:

    good i got what i wanted

  2. Mikul Gohil on May 7, 2008 at 8:11 pm said:

    ok man i will just link to your post sorry for that

  3. Mikul Gohil on May 7, 2008 at 8:12 pm said:

    sorry for that i have removed from my site

  4. Pingback: klog » Blog Archive » efficient JS minification using PHP

  5. Domi on June 26, 2008 at 3:21 pm said:

    V. cool.

    A possible refinement (depending on your js sources) is to add a semicolon on the end of each javascript file contents, for the case where the original doesn’t have it (like if it’s packed) and it runs into the next one.

    Cheers

  6. Domi, true. But also true of lines within the javascript. there’s nothing I can do about the latter without much work. and if the writer tends to finish the lines in the middle of the file with semi-colons, then my guess is that the same author would finish the last line of the script with a semi-colon.

  7. You don’t need to calculate md5, you can simply use filemtime($file_url) instead – it’s much faster!

  8. BoÅ¡tjan, that’s a good solution if there is only one javascript file (the first case described above), but if there are multiple, then you need a unique string which will cover them all (the second case).

    But yeah – I think in the first case, mtime is sufficient. You’re right.

  9. If there are more files, you can use the biggest filemtime (eg. modified time of last changed file) and it’s allmost the same. ;)

    But ms5 has an advantage: if you upload older version of the file, md5 is the same, and already cached my users browser.

  10. Excuse me for being so dumbfounded here coz. I just started coding just lately. But how can I embed the codes if my ASPX that references the javascript?

  11. M@ñ, this solution is most likely not for you. The solution I’ve posted above is specifically for PHP + Linux + Apache. You are probably using ASPX + Windows + IIS. Totally different environment.

    You would have to get help from an experienced ASPX programmer.

Post Navigation

%d bloggers like this: