Can We Prevent CSS Caching?
When you are developing a site, there is a heck of a lot of “refreshing” going on. You start to get a pretty good feel for what your browser is going to pick up on a single refresh, and what it won’t. For example, I find that if I over-write an image file on the server, it will take me two refreshes for that image to update on the live site. Then maybe I’ll pop over into Opera and see how the site is doing over there, only to find on the first render of the page that is a really old version. Uh oh. Refresh. Refresh. Oh… there it is.
That’s fine for you, you are used to that kind of thing. But what if you send that same link to your client, and the same thing happens to them. There is a good chance there will be (at the very least) some confusion. On a larger scale, lets say you roll out a fairly major layout change on a high-traffic website. A huge number of people might see a borked layout the next time they visit, because of CSS caching.
Can we solve this? Is there some way to prevent CSS caching?
Timestamping Your CSS
There is a little trick that I believe comes from the land of javascript programmers. To prevent the caching of their scripts, they add a timestamp to the end of the src link. So, let’s steal the idea.
I did a quick test to see if this kind of tomfoolery would even fly. Here is how I included the stylesheet link:
<link rel="stylesheet" type="text/css" href="style.css?<?php echo date('l jS \of F Y h:i:s A'); ?>" />
Which results in this:
<link rel="stylesheet" type="text/css" href="style.css?Thursday 24th of April 2008 04:45:21 PM" />
The theory here is that that link will change every second, and the browser will be tricked into thinking this is a new stylesheet and loading it fresh every time. Jason Edmond Beaird had the same idea and even created a little bookmarklet to force it.
So I haven’t done any hardcore testing of this, but my early tests suggest that it works pretty well. What do you think folks? Am I just drinking the kool-aid here? Are there any serious problems with doing this? Is there a better/smarter/faster way to do it?









1
You can load the expires module in apache and then add this directive:
ExpiresActive On
ExpiresDefault “access plus 10 days”
ExpiresByType text/css “access plus 1 second”
Comment by Joshua McFarren — April 25, 2008 @ 6:27 am
2
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault “access plus 10 days”
ExpiresByType text/css “access plus 1 second”
</IfModule>
Comment by Joshua McFarren — April 25, 2008 @ 6:29 am
3
I understand this method applies only for development use. I can’t think of a reason of why it should be used on an ordinary site that stays pretty much the same. Correct me if I’m wrong.
Comment by Dan — April 25, 2008 @ 6:46 am
4
Rails, I believe, does this automatically (when you use the proper tag in the view template).
In addition, if you’re using Subversion or another versioning system, you could add the version information to the end of the CSS file. That way, it only refreshes if there’s a version change, not every single time.
Comment by August Trometer — April 25, 2008 @ 6:49 am
5
It’s the best way to have a F grade in YSlow
Comment by Ced — April 25, 2008 @ 6:57 am
6
Particletree had an article on CSS/JS versioning a couple of months ago. It fits between your JS solution and August’s svn suggestion: http://particletree.com/notebook/automatically-version-your-css-and-javascript-files/
It seems this would also serve your needs as a cachebuster based on file updates rather than on each visit to the page but it wouldn’t require an svn repository.
Comment by Kyle Kinnaman — April 25, 2008 @ 7:05 am
7
@Joshua McFarren: Thanks for that tip. So I think I am misunderstanding this fundamentally. It’s the server that is caching the CSS file, not the browser? I can see how that would make sense, I just usually think of caching as a browser thing.
@Dan: Actually I think it’s even more important on production sites. Even if you rarely ever change anything, that day that you do, you have a ton of visitors likely have your old CSS cached and see borked layout.
@August: I like the versioning idea, which you could do even without subversion. But then it’s up to you to remember to do it and not something that just happens automatically.
@Ced: Is a simple PHP call like “date” really that atrociously slow? I could see maybe getting an afterschool detention for this, but an F?! =)
Comment by Chris Coyier — April 25, 2008 @ 7:20 am
8
@Chris: I’d recommend using a timestamp instead of the “English” version of the date:
style.css?4884491531235
It removes spaces from your URI.
Comment by David Walsh — April 25, 2008 @ 8:37 am
9
Get the Firefox Web Developer toolbar… you can turn off caching in there. But it’s usually not Firefox where we do most of our refreshing, is it?
The server doesn’t normally do any content caching, but when the browser sends a request header “If-Modified-Since”, the server might respond “200 OK” with the new content, or else “304 Not Modified” with no response body, which tells the browser to get it from its cache.
Another option is to set that query parameter to the timestamp of when the file was modified, or else a hash of its content:
Comment by Andrew Vit — April 25, 2008 @ 9:25 am
10
filemtime(’path_to.css’);
md5( file_get_contents(’path_to.css’) )
* tag filter stripped my PHP example… trying again.
Comment by Andrew Vit — April 25, 2008 @ 9:27 am
11
@Joshua McFarren: You actually *want* your scripts, CSS, images etc. to be cached by the visitor’s browser, or your server gets hit for every link on every page, and seriously slows down the response.
If you timestamp each page with *the time the page was last modified” then the cache will be replaced only after any update - which is surely what you want, no?
FWIW - You can still employ PHP in your pages even if they are named with an “htm” or “html” suffix. Check with your host on how to do this, and whether they allow it.
Comment by David Hucklesby — April 25, 2008 @ 9:37 am
12
Great article Chris. I’m ALWAYS giving my clients instruction on cache refreshing in IE and FF. But your trick will now negate that necessity during development.
Comment by Adrian — April 25, 2008 @ 10:35 am
13
It might be better to use filectime(’/path/to/file.css’) rather than just the current date. That would allow the browser to only download a new file when the file was, in fact, new. (filectime($file) will return the time that the file was last changed as a Unix timestamp)
so your code would become:
echo date(’l jS \of F Y h:i:s A’, filectime(’/path/to/style.css’));
To echo David Walsh I don’t really even see the point of printing the date in a human readable format, the time stamp would be just fine
so: echo filectime(’/path/to/style.css’);
Comment by Jeffrey Gould — April 25, 2008 @ 10:35 am
14
By adding the timestamp/date/something unique on each load of the page, you force a refresh. Which could be substantially more server requests than you or your user wants.
By leaving it off, you are subject to the browser/users to act however it/they do.
Therefore it seems to me what you want to do is, when you make your page go live with your new stylesheet, add your little date trick for a little bit. A week? A day? Sure the first few newcomers are hit with extra requests… but once you think most of your users have gotten that new stylesheet, you can take off the date marker again, and the extra traffic stops.
Sounds easier than it is to decide when to take it off… but it’s some compromise, right?
Though to take a different point of view, so what if the user gets “the old stylesheet” two more page views after they should? If the changes were outside just the stylesheet, the website will look messed up (not just different) and a vast majority of users will see the page totally messed up, and just refresh the page. If it’s not messed up and just “old”, well they’ll get the new one soon enough.
Lastly and I think more importantly, why don’t you just user php’s filemtime() function and so the request only changes when the modified date of the css file changes?
Comment by Alex — April 25, 2008 @ 12:15 pm
15
@Chris:
The browser is caching the CSS. Different browsers have different settings about the caching but it usually asks the server (by sending an ETag or timestamp) whether it needs to download the CSS file again or not. The server responds with code 304 (Not Modified) when certain server settings are met such as the timestamp on the files are the same or something similar and this causes your browser to use the cached copy.
You can set your server to always respond that it has a newer file version and that is what Joshua’s first comment proposes but this is only for Apache HTTPD. So basically yes there are some server settings you will want to tweak to prevent caching forever but your solution works just fine for dev. testing.
I know that is a lot and there is so much more to caching so let me know if I missed something or if you have other questions.
-Eric
Comment by Eric Wendelin — April 25, 2008 @ 12:59 pm
16
I already use a slightly saner version of this. In the html, I call a master.css
this master.css @import’s (’file-20080425.css’);
during development, I have a master-20080425.css as an alternative stylesheet, then when I’m happy with what I’ve done, I copy it over the current master.css. It means I have a lot of out-dated stylesheets floating around, but I control exactly which ones are currently being called, without making needless requests with every single pageview.
If you really need to brute-force the cache, this would be the sanest way to use your master.css?timestamp idea, as it’s only a 2-3 line file that’s going to be fetched each time. Still a wasteful http-transaction, but not extraneous amounts of bandwidth for *every single* page view.
For the apache directives, it’s not the server caching. The server has the ability to serve some extra headers that let the browser know how often this content should be re-fetched, so you’re just taking control of what hints the server is sending. It’s still up to the client whether to take the hint or not.
Comment by Shaun — April 25, 2008 @ 5:05 pm
17
I use a FireFox add-on called Fasterfox. Its simple and fast, you just right click clear cache. Then just refresh your page.
Comment by Jake-DesignerFied — April 26, 2008 @ 1:15 am
18
Hold the [SHIFT]-key while you are clicking on the refresh-button in the address bar. Most browsers recognize this as a “forced refresh” meaning they will get all files from the server again.
Comment by Paul — April 26, 2008 @ 10:38 am
19
Where I work we keep css and images on “perpetually” cached servers, so we use a versioning system to let the visitors see the modified files. Out system is quite similar to your php example, but we use perl instead. We have a variable appended to the css file path which comes out like this: <link rel=”stylesheet” type=”text/css” href=”style.css?v=1.0023″ /> for example. Every time an edited css is put in production, the version number changes, thus avoiding the previously cached file to be seen by visitors.
Comment by Flavia — April 28, 2008 @ 1:16 am
20
You may also name your CSS file with a “.php” extension.
That should allow you to insert the following code avoiding the cache:
header(”Cache-Control: no-cache, must-revalidate”); // HTTP/1.1
header(”Expires: Mon, 26 Jul 1997 05:00:00 GMT”); // Date in the past
Comment by jimmy — May 2, 2008 @ 2:07 am
21
@David Hucklesby
The apache directive that I put in my example is something that we only deploy on a server where we post proofs for clients to review. On that site we want to *ensure* that a cached page is not being displayed. I can’t tell you how many times I’ve had to explain to the client that the changes were posted and they merely need to push the refresh/reload button to see them. Once we implemented that directive I never had to explain that again.
You’re right though, you definitely would not want to implement that willy-nilly sever-wide. Its strictly for developement not depolyment.
Comment by Joshua McFarren — May 2, 2008 @ 2:55 pm
22
Umf…
CSS without cache is useless and dangerous… you’d rather use inline styles then!
Plus, your method doesn’t *prevent* caching but multiplies it (each time a page is accessed, a new file is saved in the user’s cache).
Use intelligent versioning instead. You can do it manually by adding a get parameter to your CSS URL, (for example ?version=20080505) or automatically with some PHP scripting monitoring the last-modified date of your CSS file and passing it to the CSS URL.
Comment by Thibaut Allender — May 5, 2008 @ 5:39 am