Just the other day I was presented an issue where multiple browser types on multiple flavors of OS’s were caching styles, scripts and images in a variety of ways. We had rolled out a new version of a product, which ended up having a bug in it, and we had to roll it back to a previous state in order to fix a few things and push it back out. However, due to the flip flopping back and forth depending on the OS you were on, and the browser you were using new scripts were stuck in the cache while the old version was visible, and vis versa. It was a nightmare for a short while. But the at the end of the day it was all due to caching. Not all browsers are equal even Chrome on Mac vs Chrome on Windows vs Chrome on Linux. While they hold similar techniques, each has its own quirks. However thats a discussion for another day.
So how to assess the issue. Well there are a number of ways you can do it, but again due to quirks, limitations, browser versions, os versions, and so on. Not all solutions work, be it either they don’t work at all or they only work here; but not there. One method that used to work pretty well universally was to append a parameter (query string to the end of a static file path) example:
Now the problem with this that I encountered is, not all browsers accepted this, well not many of the newer version browsers it appears. Also, I noted that running through a proxy they sometimes get lost as well. So while this was a good method for a long time, its starting to fall of the realm of possibilities. I think in part that most query strings are usually associated with dynamically generated data, which usually implies the output could change from time to time. Im sure there is more to that then that, but thats my take on it.
So, what else is there? Theres the server side headers to include at the top of your pages.
and there are meta tags
But the problem with both of those, is again fickle. Some browsers will just ignore them, and in the case of the meta tag. Im pretty sure at this point only browsers supporting that properly are IE < 7, FF < 3.5, and other older browsers. But then some will argue, why not just go the .htaccess route, and expire it that way. Well again with the browsers and the way they handled certain headers. Cause even though its in the htaccess, there is still a header passed off to the browser to trigger a reaction, of which might (not as likely) get ignored. However similar with the previous expire methods above if they work. The bigger problem is now your telling the browsers every visitor, every time, to take a fresh pull of the files. Don’t bother with the cached version it may have. Which is ok, if you have a small site, with minimal traffic. BUT, what if you don’t? or, what if what you want to essentially invoke/force a refresh on is a larger file. Not only are you going to waste bandwidth (and depending on your host provider this can become very expensive very quick). But you may leave your users twiddling there thumbs per page load which might cause them to go else where and never return. So where it might be a good idea for some I wouldn’t suggest it unless absolutely needed. Which if I were to go that route I would likely go with something to the extent of
#in your .htaccess
FileETag None Header unset ETag Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate" Header set Pragma "no-cache" Header set Expires "Tue, 14 Jan 1975 01:00:00 GMT"
So.. what would I do, what did I end up coming up with for a solution to the overall problem. Well, it turned out to be much simpler than I tought, and holds pretty well so far we have tested on Windows XP – Windows 8 with IE 7 – 10, Safari, Opera, Chrome, Firefox (various versions), tested on Mac versions 10.6 – 10.8 (again various browsers), Redhat, Ubuntu, and one other.. and again with all common and supported browsers. Of which all had cached versions of the problem we were facing. (Just for the sake of saying we have a cluster of servers with various vm’s for QA purposes, just in case you are wondering how we tested all mentioned)
So the end result
RewriteEngine On RewriteRule ^__ver[0-9]+__/(.*)$ /$1 [QSA]
Which is a mix match of the first method where we appended a query string to the end of the path, however in this method we are tricking browser into thinking its in the path, so this way it gets cached as such. So if the path changes (and it will) the browser will ask for a new copy. So lets say I had a css file, that was in my assets/css/ directory in order for this to work, in my HTML I would simply put as the path assets/css/__ver1__/style.css for the sake of this example. Which like I said fools the browser into thinking the version is part of the path, without actually changing the directory structure to support it on the server. But the server is told to rewrite it and treat it just like assets/css/style.css
Worth mentioning: You don’t have to have __ver123__ you can do as you wish. Just make sure to adapt what you want to do as your delimiter is unique and put it in the rule above. Also if you do use my above example, the example uses two (2) _ on either side of the ver123 part
Well hope this helps someone like it did me. Enjoy..