Grow your CSS skills. Land your dream job.

Can We Prevent CSS Caching?

Published by Chris Coyier

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 attribute. 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" />

Alter that date format as needed. You might wanna skip spaces.

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.

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?

Update

I'm updating this in June 2013 here just to point out a few important things.

In production, breaking cache when you push out new CSS is a very good plan. But breaking cache with a date stamp like presented here would mean you gain no benefit at all from browser caching which would be horrendous for performance. You'd be better off doing something like adding a version number to the end of the file name like style.css?version=3.2 that you update as needed.

Locally in development, breaking cache every page load might be OK, but modern development tools like Chrome's web inspector can turn off caching anyway.

Comments

  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”

  2. <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresDefault “access plus 10 days”
        ExpiresByType text/css “access plus 1 second”
    </IfModule>

  3. Permalink to comment#

    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.

  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.

  5. Permalink to comment#

    It’s the best way to have a F grade in YSlow :-)

  6. @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?! =)

  7. @Chris: I’d recommend using a timestamp instead of the “English” version of the date:

    style.css?4884491531235

    It removes spaces from your URI.

  8. 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:

  9. filemtime(‘path_to.css’);
    md5( file_get_contents(‘path_to.css’) )

    * tag filter stripped my PHP example… trying again.

  10. @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.

  11. Permalink to comment#

    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.

  12. 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’);

  13. Alex
    Permalink to comment#

    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?

  14. @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

  15. Shaun
    Permalink to comment#

    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.

  16. I use a FireFox add-on called Fasterfox. Its simple and fast, you just right click clear cache. Then just refresh your page.

  17. Paul
    Permalink to comment#

    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.

  18. Flavia
    Permalink to comment#

    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.

  19. 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

  20. @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.

  21. 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.

  22. Carl Armbruster

    We did something very similar. We use Oracle and JSP. I created a Java stored procedure that appends the date/time stamp of the file (as a long value) as the version string. This way, it is automatically changed anytime the last modified date/time of the file changes. The JS and CSS is still cached but when a change is made, the client browser requests the new file so the user sees the updated files.

  23. Well, you saved my rear, and a lot of frustration! Used your example above, along with the current ‘time stamp’, and it works like a charm!!! Yippee! Thank You!

    At least now I know that my site will always look the way it is intended.

    And to give feedback to the person above that mentioned that it seems to only be a good idea for developmental reasons -refer to the line above.

    When you’re maintaining a site that requires constant changing, the CSS file is the #1 change, and when that does not refresh automatically (as I have my actual pages set to do), it sux! Heck, I’ve had my clients come back to me and say they cannot see the changes I’ve made; so this little code is the greatest thing I’ve run across in a long time!

    Thanks a million!
    Trace

  24. Trace, why don’t you read the comments? I mean, all of them…

  25. Baldur
    Permalink to comment#

    works also for all other file types

    ExpiresActive on
    ExpiresDefault “access plus 8 hours”
    ExpiresByType text/html “access plus 1 hours”
    ExpiresByType text/xml “access plus 2 hours”
    ExpiresByType text/css “access plus 3 hours”
    ExpiresByType text/plain “access plus 4 hours”
    ExpiresByType application/x-httpd-php “access plus 5 hours”
    ExpiresByType application/x-javascript “access plus 6 hours”
    ExpiresByType application/pdf “access plus 7 hours”
    ExpiresByType image/gif “access plus 8 hours”
    ExpiresByType image/png “access plus 9 hours”
    ExpiresByType image/jpeg “access plus 10 hours”
    ExpiresByType image/x-icon “access plus 11 hours”

  26. This article helped me to fix my zend site issue. I developed the site in ZF and for UI i used boot strap. I updated the site last week, and until the css was not taking effect and was too wired for me, as i also cleared browsers cache and still it was not taking effect :( . Finally i got this article and it helped me and my issue is fixed in a minute. Thank you for this great article.

  27. Hey everyone, hope you all scroll down to read this. Everyone is making this wayyyyyy too complicated. There is a VERY simple function built right into PHP that will allow you to update any file any time you want (in this case your CSS files) and the only time the file name will change (and prompt a new DL to wipe out the old cache) is when you modify it. Here ya go!

    <link rel="stylesheet" href="<?php bloginfo('template_url'); ?>/style.css?<?php echo filemtime(TEMPLATEPATH . '/style.css'); ?>" type="text/css" media="screen, projection" />

    Notice it? “filemtime” will stamp the CSS file with a value specific to the last time the file was modified. Set your CSS to cache for life and the file will remain cached until you modify it, which will prompt the new ID and the new DL for cache. This is the most efficient way I know to be able to modify files freely without having to worry about overriding or losing the benefit of your cache settings.

    Good luck all! (Sorry moderator, resubmitted cause I forgot to put the code in an inline marker and it messed up!)

    • h_driver

      I scrolled. :) Thanks for the tip – no doubt this is the best and most efficient way to do this in terms of server-side speed.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".