an ordinary blog — andr3.net

rss feed

my lifestream rss feed

are you tired of this? then return to latest posts

Keeping your CSS fresh and cached

I don't know if you have this problem or not, but at least around here i've had to update my CSS file quite a couple of times, specially in the beginning since i was using new elements in my posts that weren't already styled. For example, when i wrote my first post about a movie, i had never styled the rating box before because i hadn't thought of it, so I had to add that to my posts.css.

Yeah, so?

Well, for the first times that a user saw that post he was likely to see the box incorrectly since the div was unstyled, because his browser was using the copy in cache. Not until that copy was refreshed did he see the correct style.


A couple of days later, i kept running into the same problem and in a conversation with João we ended up with a temporary solution. Adding a query string to the URI would force the browser to update the CSS. I attached a string which was basically the UNIX timestamp of the current time.

<link rel="stylesheet" type="text/css" href="posts.css?1127086606" />

Are you crazy? Caching is the whole point!

I know! Every time the browser opened the page it would have to fetch the CSS file and not use the copy in cache. This would mean the loss of one of the inherent advantages of using Cascading Style Sheets.

Now i don't update my CSS that often, but sometimes i still need to fix something and one of the solutions would be to add a fixed string instead of a dynamic timestamp. But that was too "manual" for my taste, so i wrote a little php script.

PHP script to force the CSS reload

First, specify the CSS files. I have two, style.css and posts.css.

$csses = Array ( "./css/style.css", "./css/posts.css" );

Now using the function filemtime() i'll fetch the last modified time of each file.

$css_files = Array();
foreach( $csses as $css )
    if( file_exists($css) )
        $css_files[$css] = date( "dmY-His", filemtime( $css ) );

Now you can use these values and append them to the CSS declaration in your (X)HTML document.

foreach( $css_files as $file => $mtime )
    echo "<link rel="stylesheet" type="text/css" href="$file?$mtime" />
";


There, that's it! Simple, right? Here's the example script. Now every time you change your CSS, the PHP will automatically append a different string which will force the browser to update the file but still use the cache in the following hits.

Testing

Now i needed to make sure this technique worked, so i took advantage of having a Linux box acting has my router and filtered the TCP packets using tcpdump to see how many times the browser went to the server to fetch the file.

Before anything else I had to try this in the "old fashioned" way... with no query string, simply the name of the file (...) href="posts.css" (...). If you want to see the obvious output, here it is. Full output.

Test Case #1

First, i ran it using the temporary solution. Adding the timestamp as an argument. Here's the output:

root@thegodfather:~ # tcpdump -i eth0 -x host andr3.net | grep posts.css?
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
page reload
0x0030: 2f70 6f73 7473 2e63 7373 3f31 3132 3730 /posts.css?11270
link clicked
0x0030: 2f70 6f73 7473 2e63 7373 3f31 3132 3730 /posts.css?11270
link clicked
0x0030: 2f70 6f73 7473 2e63 7373 3f31 3132 3730 /posts.css?11270

(full output)
As you can see, every time i clicked a link, the browser made a request.

Test Case #2

Now, with the last modified time appended... (the php script solution i described above)

root@thegodfather:~ # tcpdump -i eth0 -x host andr3.net | grep posts.css?
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
page reload
0x0030: 2f70 6f73 7473 2e63 7373 3f31 3830 3932 /posts.css?18092
link clicked
link clicked
link clicked
link clicked
link clicked
link clicked
css file updated.
link clicked
0x0030: 2f70 6f73 7473 2e63 7373 3f31 3830 3932 /posts.css?18092
link clicked
link clicked

(full output)
Now the browser only fetched the file when the CSS was updated, just like I wanted.

Thoughts?

Let me know if i'm doing something wrong or if this will help you. I know it really helps me to ensure that the visitors to my website always get the freshest version of the CSS.

Comments

↑ top