How to force browser to reload cached CSS/JS files?

    |
  • Added:
  • |
  • In: Wordpress

I have noticed that some browsers (in particular, Firefox and Opera) are very zealous in using cached copies of .css and .js files, even between browser sessions. This leads to a problem when you update one of these files but the user's browser keeps on using the cached copy.

The question is: what is the most elegant way of forcing the user's browser to reload the file when it has changed?

Ideally the solution would not force the browser to reload the file on every visit to the page. I will post my own solution as an answer, but I am curious if anyone has a better solution and I'll let your votes decide.

Update:

After allowing discussion here for a while, I have found John Millikin and da5id's suggestion to be useful. It turns out there is a term for this: auto-versioning.

I have posted a new answer below which is a combination of my original solution and John's suggestion.

Another idea which was suggested by SCdF would be to append a bogus query string to the file. (Some Python code to automatically use the timestamp as a bogus query string was submitted by pi.). However, there is some discussion as to whether or not the browser would cache a file with a query string. (Remember, we want the browser to cache the file and use it on future visits. We only want it to fetch the file again when it has changed.)

Since it is not clear what happens with a bogus query string, I am not accepting that answer.

This Question Has 30 Answeres | Orginal Question | Kip

If you're using git + php, you can reload the script from cache each time there is a change in the git repo, using the following code:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog); echo ' <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL; 

Interesting post. Having read all the answers here combined with the fact that I have never had any problems with "bogus" query strings (which I am unsure why everyone is so reluctant to use this) I guess the solution (which removes the need for apache rewrite rules as in the accepted answer) is to compute a short HASH of the CSS file contents (instead of the file datetime) as a bogus querystring.

This would result in the following:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" /> 

Of course the datetime solutions also get the job done in the case of editing a CSS file but I think it is about the css file content and not about the file datetime, so why get these mixed up?

Another suggestion for ASP.Net websites,

  1. Set different cache-control:max-age values, for different static files.
  2. For css/js files, the chances of modifying these files on server is high, so set a minimal cache-control:max-age value of 1 or 2 mins or something that meets your need.
  3. For images, set a far date as cache-control:max-age value, say 360 days.
  4. By doing so, when we make the first request, all static contents are downloaded to client machine with a 200-OK response.
  5. On subsequent requests and after two minutes, we see 304-Not Modified requests on css and js files which avoids us from css/js versioning.
  6. Image files will not be requested as they will be used from cached memory til the cache expires.
  7. By using below web.config configurations, we can achieve the above described behavior,

It seems all answers here suggest some sort of versioning in the naming scheme, which has its downsides.

Browsers should be well aware of what to cache and what not to cache by reading the webservers response, in particular the http headers - for how long is this resource valid ? was this resource updated since I last retrieved it ? etcetera.

If things are configured 'correctly', just updating the files of your application should (at some point) refresh the browsers caches. You can for example configure your web server to tell the browser to never cache files (which is a bad idea).

A more in-depth explanation of how that works is here https://www.mnot.net/cache_docs/#WORK

If you are using a modern browser, you could use a manifest file to inform the browsers which files need to be updated. This requires no headers, no versions in urls etc...

For more details, see: See: https://developer.mozilla.org/nl/docs/Web/HTML/Applicatie_cache_gebruiken#Introduction

I'm adding this answer as a SilverStripe http://www.silverstripe.org specific answer which I was looking for and never found but have worked out from reading: http://api.silverstripe.org/3.0/source-class-SS_Datetime.html#98-110

Hopefully this will help someone using a SilverStripe template and trying to force reload a cached image on each page visit / refresh. In my case it is a gif animation which only plays once and therefor did not replay after it was cached. In my template I simply added:

?$Now.Format(dmYHis) 

to the end of the file path to create a unique time stamp and to force the browser to treat it as a new file.

Sorry for bringing back a dead thread.

@TomA is right.

Using "querystring" method will not be cached as quoted by Steve Souders below:

...that Squid, a popular proxy, doesn’t cache resources with a querystring.

@TomA suggestion of using style.TIMESTAMP.css is good, but MD5 would be much better as only when the contents were genuinely changed, the MD5 changes as well.

The simplest method is to take advantage of the PHP file read functionality. Just have the PHP echo the contents of the file into tags.

<?php //Replace the 'style.css' with the link to the stylesheet. echo "<style type='text/css'>".file_get_contents('style.css')."</style>"; ?> 

If you're using something besides PHP, there are some variations depending on the language, but almost all languages have a way to print the contents of a file. Put it in the right location (in the section), and that way, you don't have to rely on the browser.

For a Java Servlet environment, you can look at the Jawr library. The features page explains how it handles caching:

Jawr will try its best to force your clients to cache the resources. If a browser asks if a file changed, a 304 (not modified) header is sent back with no content. On the other hand, with Jawr you will be 100% sure that new versions of your bundles are downloaded by all clients. Every URL to your resources will include an automatically generated, content-based prefix that changes automatically whenever a resurce is updated. Once you deploy a new version, the URL to the bundle will change as well so it will be impossible that a client uses an older, cached version.

The library also does js/css minification, but you can turn that off if you don't want it.

"Another idea which was suggested by SCdF would be to append a bogus query string to the file. (Some Python code to automatically use the timestamp as a bogus query string was submitted by pi.) However, there is some discussion as to whether or not the browser would cache a file with a query string. (Remember, we want the browser to cache the file and use it on future visits. We only want it to fetch the file again when it has changed.) Since it is not clear what happens with a bogus query string, I am not accepting that answer."

<link rel="stylesheet" href="file.css?<?=hash_hmac('sha1', session_id(), md5_file("file.css")); ?>" />

Hashing the file means when it has changed, the query string will have changed. If it hasn't, it will remain the same. Each session forces a reload too.

Optionally, you can also use rewrites to cause the browser to think it's a new URI

I see a problem with the approach of using a timestamp- or hash-based differentiator in the resource URL which gets stripped out on request at the server. The page that contains the link to e.g. the style sheet might get cached as well. So the cached page might request an older version of the style sheet but will be served the latest version, which might or might not work with the requesting page.

To fix this, you either have to guard the requesting page with a no-cache header or meta, to make sure it gets refreshed on every load. Or you have to maintain all versions of the style file that you ever deployed on the server, each as an individual file and with their differentiator intact, so that the requesting page can get at the version of the style file it was designed for. In the latter case you basically tie the versions of the HTML page and the style sheet together, which can be done statically and doesn't require any server logic.

The RewriteRule needs a small update for js or css files that contain a dot notation versioning at the end. E.g. json-1.3.js.

I added a dot negation class [^.] to the regex so .number. is ignored.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L] 

For ASP.NET 4.5 and greater you can use script bundling.

The request http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81 is for the bundle AllMyScripts and contains a query string pair v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. The query string v has a value token that is a unique identifier used for caching. As long as the bundle doesn't change, the ASP.NET application will request the AllMyScripts bundle using this token. If any file in the bundle changes, the ASP.NET optimization framework will generate a new token, guaranteeing that browser requests for the bundle will get the latest bundle.

There are other benefits to bundling including increased performance on first time page loads with minification.

For ASP.NET I suppose next solution with advanced options (debug/release mode, versions):

Js or Css files included by such way:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" /> <link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" /> 

Global.JsPostfix and Global.CssPostfix is calculated by the following way in Global.asax:

protected void Application_Start(object sender, EventArgs e) { ... string jsVersion = ConfigurationManager.AppSettings["JsVersion"]; bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]); int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision; JsPostfix = ""; #if !DEBUG JsPostfix += ".min"; #endif JsPostfix += ".js?" + jsVersion + "_" + buildNumber; if (updateEveryAppStart) { Random rand = new Random(); JsPosfix += "_" + rand.Next(); } ... } 

Update: Rewritten to incorporate suggestions from John Millikin and da5id. This solution is written in PHP, but should be easily adapted to other languages.

Update 2: Incorporating comments from Nick Johnson that the original .htaccess regex can cause problems with files like json-1.3.js. Solution is to only rewrite if there are exactly 10 digits at the end. (Because 10 digits covers all timestamps from 9/9/2001 to 11/20/2286.)

First, we use the following rewrite rule in .htaccess:

RewriteEngine on RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L] 

Now, we write the following PHP function:

/** * Given a file, i.e. /css/base.css, replaces it with a string containing the * file's mtime, i.e. /css/base.1221534296.css. * * @param $file The file to be loaded. Must be an absolute path (i.e. * starting with slash). */ function auto_version($file) { if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file)) return $file; $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file); return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file); } 

Now, wherever you include your CSS, change it from this:

<link rel="stylesheet" href="/css/base.css" type="text/css" /> 

To this:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" /> 

This way, you never have to modify the link tag again, and the user will always see the latest CSS. The browser will be able to cache the CSS file, but when you make any changes to your CSS the browser will see this as a new URL, so it won't use the cached copy.

This can also work with images, favicons, and JavaScript. Basically anything that is not dynamically generated.

Here is a pure JavaScript solution

(function(){ // Match this timestamp with the release of your code var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10); var lastCacheDateTime = localStorage.getItem('lastCacheDatetime'); if(lastCacheDateTime){ if(lastVersioning > lastCacheDateTime){ var reload = true; } } localStorage.setItem('lastCacheDatetime', Date.now()); if(reload){ location.reload(true); } })(); 

The above will look for the last time the user visited your site. If the last visit was before you released new code, it uses location.reload(true) to force page refresh from server.

I usually have this as the very first script within the <head> so it's evaluated before any other content loads. If a reload needs to occurs, it's hardly noticeable to the user.

I am using local storage to store the last visit timestamp on the browser, but you can add cookies to the mix if you're looking to support older versions of IE.

Simple Client-side Technique

In general, caching is good.. So there are a couple of techniques, depending on whether you're fixing the problem for yourself as you develop a website, or whether you're trying to control cache in a production environment.

General visitors to your website won't have the same experience that you're having when you're developing the site. Since the average visitor comes to the site less frequently (maybe only a few times each month, unless you're a Google or hi5 Networks), then they are less likely to have your files in cache, and that may be enough. If you want to force a new version into the browser, you can always add a query string to the request, and bump up the version number when you make major changes:

<script src="/myJavascript.js?version=4"></script> 

This will ensure that everyone gets the new file. It works because the browser looks at the URL of the file to determine whether it has a copy in cache. If your server isn't set up to do anything with the query string, it will be ignored, but the name will look like a new file to the browser.

On the other hand, if you're developing a website, you don't want to change the version number every time you save a change to your development version. That would be tedious.

So while you're developing your site, a good trick would be to automatically generate a query string parameter:

<!-- Development version: --> <script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script> 

Adding a query string to the request is a good way to version a resource, but for a simple website this may be unnecessary. And remember, caching is a good thing.

It's also worth noting that the browser isn't necessarily stingy about keeping files in cache. Browsers have policies for this sort of thing, and they are usually playing by the rules laid down in the HTTP specification. When a browser makes a request to a server, part of the response is an EXPIRES header.. a date which tells the browser how long it should be kept in cache. The next time the browser comes across a request for the same file, it sees that it has a copy in cache and looks to the EXPIRES date to decide whether it should be used.

So believe it or not, it's actually your server that is making that browser cache so persistent. You could adjust your server settings and change the EXPIRES headers, but the little technique I've written above is probably a much simpler way for you to go about it. Since caching is good, you usually want to set that date far into the future (a "Far-future Expires Header"), and use the technique described above to force a change.

If you're interested in more info on HTTP or how these requests are made, a good book is "High Performance Web Sites" by Steve Souders. It's a very good introduction to the subject.

Thanks at Kip for his perfect solution!

I extended it to use it as an Zend_view_Helper. Because my client run his page on a virtual host I also extended it for that.

Hope it helps someone else too.

/** * Extend filepath with timestamp to force browser to * automatically refresh them if they are updated * * This is based on Kip's version, but now * also works on virtual hosts * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files * * Usage: * - extend your .htaccess file with * # Route for My_View_Helper_AutoRefreshRewriter * # which extends files with there timestamp so if these * # are updated a automatic refresh should occur * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L] * - then use it in your view script like * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css')); * */ class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract { public function autoRefreshRewriter($filePath) { if (strpos($filePath, '/') !== 0) { // path has no leading '/' return $filePath; } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) { // file exists under normal path // so build path based on this $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath); return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath); } else { // fetch directory of index.php file (file from all others are included) // and get only the directory $indexFilePath = dirname(current(get_included_files())); // check if file exist relativ to index file if (file_exists($indexFilePath . $filePath)) { // get timestamp based on this relativ path $mtime = filemtime($indexFilePath . $filePath); // write generated timestamp to path // but use old path not the relativ one return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath); } else { return $filePath; } } } } 

Cheers and thanks.

The 30 or so existing answers are great advice for a circa 2008 website. However, when it comes to a modern, single page application (SPA), it might be time to re-think some fundamental assumptions… specifically the idea that it is desirable for the web server to serve only the single, most recent version of a file.

Imagine you're a user that has version M of a SPA loaded into your browser:

  1. Your CD pipeline deploys the new version N of the application onto the server
  2. You navigate within the SPA, which sends an XHR to the server to get /some.template
    • (Your browser hasn't refreshed the page, so you're still running version M)
  3. The server responds with the contents of /some.template — do you want it to return version M or N of the template?

If the format of /some.template changed between versions M and N (or the file was renamed or whatever) you probably don't want version N of the template sent to the browser that's running the old version M of the parser.†

Web apps run into this issue when two conditions are met:

  • Resources are requested asynchronously sometime after the initial page load
  • The app logic assumes things (that may change in future versions) about resource content

Once your app needs to serve up multiple versions in parallel, solving caching and "reloading" becomes trivial:

  1. Install all site files into versioned dirs: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
  2. Set HTTP headers to let browsers cache files forever
    • (Or better yet, put everything in a CDN)
  3. Update all <script> and <link> tags, etc. to point to that file in one of the versioned dirs

That last step sounds tricky, as it could require calling a URL builder for every URL in your server-side or client-side code. Or you could just make clever use of the <base> tag and change the current version in one place.

† One way around this is to be aggressive about forcing the browser to reload everything when a new version is released. But for the sake of letting any in-progress operations to complete, it may still be easiest to support at least two versions in parallel: v-current and v-previous.

I put an MD5 hash of the file's contents in its URL. That way I can set a very long expiration date, and don't have to worry about users having old JS or CSS.

I also calculate this once per file at runtime (or on file system changes) so there's nothing funny to do at design time or during the build process.

If you're using ASP.NET MVC then you can check out the code in my other answer here.

Have not found the client sided DOM approach creating the script node (or css) element dynamically:

<script> var node = document.createElement("script"); node.type = "text/javascript"; node.src = 'test.js?'+Math.floor(Math.random()*999999999); document.getElementsByTagName("head")[0].appendChild(node); </script> 

For my development, I find that chrome has a great solution.

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

With developer tools open, simply long click the refresh button and let go once you hover over "Empty Cache and Hard Reload".

This is my best friend, and is a super light weight way to get what you want!

Google's mod_pagespeed plugin for apache will do auto-versioning for you. It's really slick.

It parses HTML on its way out of the webserver (works with PHP, rails, python, static HTML -- anything) and rewrites links to CSS, JS, image files so they include an id code. It serves up the files at the modified URLs with a very long cache control on them. When the files change, it automatically changes the URLs so the browser has to re-fetch them. It basically just works, without any changes to your code. It'll even minify your code on the way out too.

Well, i have make it work my way by changing js version each time the page load by adding a random number to js file version as follow:

// Add it to the top of the page <?php srand(); $random_number = rand(); ?> 

Then apply the random number to the js version as follow:

<script src="file.js?version=<?php echo $random_number;?>"></script> 

Not sure why you guys are taking so much pain to implement this solution.

All you need to do if get the file's modified timestamp and append it as a querystring to the file

In PHP i would do it as:

<link rel="stylesheet" href="mycss.css?v=<?php echo filemtime('mycss.css') ?>"/> 

filemtime is a PHP function that returns the file modified timestamp.

In Laravel (PHP) we can do it in following clear and elegant way (using file modification timestamp):

<script src="{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script> 

And similar for CSS

<link rel="stylesheet" href="{{asset('css/your.css?v='.filemtime('css/your.css'))}}"> 

I came to this question when looking for a solution for my SPA, which only has a single index.html listing all the necessary files. While I got some leads that helped me, I could not find a quick and easy solution.

In the end, I wrote a quick page (including all of the code) necessary to autoversion an html/js index.html as part of the publishing process. It works perfectly and only updates new files based on date last modified.

You can see my post at http://blueskycont.com/wp/2016/05/12/autoversion-your-spa-index-html/. There is a free working winapp there too.

The guts of the code is

 private void ParseIndex(string inFile, string addPath, string outFile) { string path = Path.GetDirectoryName(inFile); HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument(); document.Load(inFile); foreach (HtmlNode link in document.DocumentNode.Descendants("script")) { if (link.Attributes["src"]!=null) { resetQueryString(path, addPath, link, "src"); } } foreach (HtmlNode link in document.DocumentNode.Descendants("link")) { if (link.Attributes["href"] != null && link.Attributes["type"] != null) { if (link.Attributes["type"].Value == "text/css" || link.Attributes["type"].Value == "text/html") { resetQueryString(path, addPath, link, "href"); } } } document.Save(outFile); MessageBox.Show("Your file has been processed.", "Autoversion complete"); } private void resetQueryString(string path, string addPath, HtmlNode link, string attrType) { string currFileName = link.Attributes[attrType].Value; string uripath = currFileName; if (currFileName.Contains('?')) uripath = currFileName.Substring(0, currFileName.IndexOf('?')); string baseFile = Path.Combine(path, uripath); if (!File.Exists(baseFile)) baseFile = Path.Combine(addPath, uripath); if (!File.Exists(baseFile)) return; DateTime lastModified = System.IO.File.GetLastWriteTime(baseFile); link.Attributes[attrType].Value = uripath + "?v=" + lastModified.ToString("yyyyMMddhhmm"); } 

You could simply add some random number with the CSS/JS url like

example.css?randomNo=Math.random() 

Many answers here advocate adding a timestamp to the url. Unless you are modifying your production files directly, the file's timestamp is not likely to reflect the time when a file was changed. In most cases this will cause the url to change more frequently than the file itself. This is why you should use a fast hash of the file's contents such has MD5 as levik and others have suggested.

Keep in mind that the value should be calculated once at build or run, rather than each time the file is requested.

As an example, here's a simple bash script that reads a list of filenames from stdin and writes a json file containing hashes to stdout:

#!/bin/bash # create a json map from filenames to md5s # run as hashes.sh < inputfile.list > outputfile.json echo "{" delim="" while read l; do echo "$delim\"$l\": \"`md5 -q $l`\"" delim="," done echo "}" 

This file could then be loaded at server startup and referenced instead of reading the file system.

Another way I have not seen mentioned for js files would be to use the jQuery $.getScript in conjunction with $.ajaxSetup option cache: false.

Instead of:

<script src="scripts/app.js"></script> 

You can use:

$.ajaxSetup({ cache: false }); $.getScript('scripts/app.js'); // GET scripts/app.js?_1391722802668 

Search
I am...

Sajjad Hossain

I have five years of experience in web development sector. I love to do amazing projects and share my knowledge with all.

NEED HOSTING?
Are you searching for Good Quality hosting?
You can Try It!
Connect Social With PHPAns
Top