633

We are currently working in a private beta and so are still in the process of making fairly rapid changes, although obviously as usage is starting to ramp up, we will be slowing down this process. That being said, one issue we are running into is that after we push out an update with new JavaScript files, the client browsers still use the cached version of the file and they do not see the update. Obviously, on a support call, we can simply inform them to do a ctrlF5 refresh to ensure that they get the up-to-date files from the server, but it would be preferable to handle this before that time.

Our current thought is to simply attach a version number onto the name of the JavaScript files and then when changes are made, increment the version on the script and update all references. This definitely gets the job done, but updating the references on each release could get cumbersome.

As I'm sure we're not the first ones to deal with this, I figured I would throw it out to the community. How are you ensuring clients update their cache when you update your code? If you're using the method described above, are you using a process that simplifies the change?

Jason Plank
  • 2,322
  • 4
  • 29
  • 39
AdamB
  • 7,780
  • 4
  • 19
  • 13
  • 2
    http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files – understack Jan 23 '12 at 09:50

28 Answers28

568

As far as I know a common solution is to add a ?<version> to the script's src link.

For instance:

<script type="text/javascript" src="myfile.js?1500"></script>

I assume at this point that there isn't a better way than find-replace to increment these "version numbers" in all of the script tags?

You might have a version control system do that for you? Most version control systems have a way to automatically inject the revision number on check-in for instance.

It would look something like this:

<script type="text/javascript" src="myfile.js?$$REVISION$$"></script>

Of course, there are always better solutions like this one.

Marco Bonelli
  • 48,251
  • 16
  • 95
  • 101
Huppie
  • 10,633
  • 3
  • 29
  • 34
  • 5
    Does anyone know if IE7 ignores this? It seems to be ignoring the appended data and using the cached file when I test in IE8 comparability view. – Shane Reustle Jan 20 '11 at 20:18
  • 4
    I always knew the query strings are key-value pair as in ?ver=123. Thanks! :) – Ankur-m Feb 15 '12 at 11:41
  • So when you add a higher javascript version it will automatically download to the client's cache? Or am i understanding this in a wrong way? – Warre Buysse Jul 20 '13 at 14:12
  • 6
    i think it's not about higher or lower version number but about changing the appended variables value to something the browser couldn't have cached yet. – mgherkins Aug 05 '13 at 10:43
  • 2
    We recently encountered the same issue, and the best i could come up with was a simple function that attached "?mod=123456" where 123456 was the unix timestamp of the modified date on the file. That seems to fix the issues while still allowing for caching where appropriate. However, I have still seen browsers flat out ignore this directive and use old JS anyway, but I don't know that there's an elegant "full fix." – VPel Sep 30 '13 at 22:13
  • 40
    For awareness: this is considered to be a hack. This method tricks the browser into thinking that a new file is being specified, as it simply looks at the full file name without interpreting it. `foo.js?1` is not the same name as `foo.js?2`, so the browser will think they are two different files. One downside is that both files will simultaneously exist in the users' cache, taking up unnecessary space. – Lee White Sep 18 '14 at 13:22
  • If you are using grunt, you could use (a.o.) https://www.npmjs.com/package/grunt-cache-breaker – David Bulté Jun 05 '15 at 12:09
  • Thanks, I am using .js files to store text and this worked well (file sizes are less then 1K). I used the simple epoch time in perl $version = time; and then put $version in the javascript at run time. So every second generates a unique "version" – Westrock Mar 06 '16 at 23:02
  • Another way is automatize a task (gulp/grunt) in your builds (files optimization) to append a hash to your scripts files. This node mudule [gulp-rev](https://github.com/sindresorhus/gulp-rev) help you to accomplish it with easy. [More details](http://stackoverflow.com/questions/31850824/angularjs-force-browser-to-clear-cache/37766386#37766386) – aUXcoder Oct 26 '16 at 18:57
  • 4
    @LeeWhite Both files will be cached in the browser no matter how you approach the problem. Either because they have different request params or different paths. So I don't think that's a disadvantage of the request param approach. – Planky Nov 29 '16 at 18:29
  • While this solution worked locally for us during testing, on the live environment the browser still seems to cache js files, Chrome in our case. – Arkiliknam Jul 26 '17 at 14:03
  • For TortoiseSVN using, the keyword is: $WCREV$ so in the above example it would be: `` and you would call SubWCRev.exe in your build step. This would set the above to something like `` where 23863 is the current SVN rev. I imagine there is an equivalent in Git. – Jeff Mergler Aug 17 '18 at 15:32
  • I have found some browsers even Chrome just don't see this as being new and keep reloading the old file. I went with a different solution for my needs I just renamed the script from whatever.js to whatever.1.1.js and changed the name in the footer load to the same and the new file is fetched maybe not the greatest but it works and stops eating my time – MitchellK Apr 21 '21 at 15:26
99

Appending the current time to the URL is indeed a common solution. However, you can also manage this at the web server level, if you want to. The server can be configured to send different HTTP headers for javascript files.

For example, to force the file to be cached for no longer than 1 day, you would send:

Cache-Control: max-age=86400, must-revalidate

For beta, if you want to force the user to always get the latest, you would use:

Cache-Control: no-cache, must-revalidate
Chase Seibert
  • 15,061
  • 5
  • 48
  • 58
  • 4
    can you please be more specific? – Kreker Jun 21 '12 at 07:44
  • 7
    He is talking about the headers sent by the web server for each file. Should be configurable in Apache for example. I think this would be the best approch – Pierre de LESPINAY Jul 25 '12 at 12:16
  • 2
    where do you configure this? – Diego Dec 29 '16 at 16:07
  • 2
    For a development webapp, it's maybe a good solution. For a production site, where you do not want to invalidate cache forever, it's not a good solution unless you *know* that each and every target client browser has come to the site. It makes me think of a potential web server feature: adapt the max-age parameter according to a configured deployment date. That would be awesome. – Claude Brisson Mar 15 '17 at 13:48
  • Chrome REQUIRES these settings in order to cache properly. Without them, Chrome will cache a file forever. Mozilla uses a much more reasonable default. See more at: https://agiletribe.wordpress.com/2018/01/29/caching-for-chrome/ – AgilePro Jan 30 '18 at 23:01
  • A little contribution: “no-cache” does not mean that there is no cache at all, it simply tells the browser to validate resources on the server before use it from the cache. According to: https://medium.com/pixelpoint/best-practices-for-cache-control-settings-for-your-website-ff262b38c5a2 – Luiz Rossi Oct 09 '19 at 17:40
  • `Cache-Control: no-cache, must-revalidate` **doesn't** mean always get latest. It simply says check the ETag and Last-Modified, and refetch if they don't match. On IIS, this is the perfect solution because the ETag always gets sent as a hash of the file contents (So if the file changes, the client will auto-update) – Coruscate5 Dec 29 '20 at 18:48
44

Google Page-Speed: Don't include a query string in the URL for static resources. Most proxies, most notably Squid up through version 3.0, do not cache resources with a "?" in their URL even if a Cache-control: public header is present in the response. To enable proxy caching for these resources, remove query strings from references to static resources, and instead encode the parameters into the file names themselves.

In this case, you can include the version into URL ex: http://abc.com/v1.2/script.js and use apache mod_rewrite to redirect the link to http://abc.com/script.js. When you change the version, client browser will update the new file.

  • I tried the ? solution and in IE8 and I get a javascript error. Mod rewrite is an option but in most cases we wont have that much control over the server. I would prefer appending the version in the js file itself or having a folder for each version – Karthik Sankar Jan 29 '14 at 20:29
  • @Hắc Huyền Minh: But when the script shall be reloaded, it should not be reloaded from the proxy-cache... – Stefan Steiger Dec 11 '17 at 17:16
35

This usage has been deprected: https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

This answer is only 6 years late, but I don't see this answer in many places... HTML5 has introduced Application Cache which is used to solve this problem. I was finding that new server code I was writing was crashing old javascript stored in people's browsers, so I wanted to find a way to expire their javascript. Use a manifest file that looks like this:

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

and generate this file with a new time stamp every time you want users to update their cache. As a side note, if you add this, the browser will not reload (even when a user refreshes the page) until the manifest tells it to.

sk8terboi87 ツ
  • 3,119
  • 2
  • 31
  • 44
amos
  • 4,234
  • 3
  • 27
  • 34
33

How about adding the filesize as a load parameter?

<script type='text/javascript' src='path/to/file/mylibrary.js?filever=<?=filesize('path/to/file/mylibrary.js')?>'></script>

So every time you update the file the "filever" parameter changes.

How about when you update the file and your update results in the same file size? what are the odds?

Erik Corona
  • 438
  • 4
  • 4
19

Not all browsers cache files with '?' in it. What I did to make sure it was cached as much as possible, I included the version in the filename.

So instead of stuff.js?123, I did stuff_123.js

I used mod_redirect(I think) in apache to to have stuff_*.js to go stuff.js

Aliaksandr Sushkevich
  • 7,264
  • 6
  • 29
  • 36
12

For ASP.NET pages I am using the following

BEFORE

<script src="/Scripts/pages/common.js" type="text/javascript"></script>

AFTER (force reload)

<script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script>

Adding the DateTime.Now.Ticks works very well.

Ravi Ram
  • 22,578
  • 20
  • 69
  • 97
  • 33
    This one goes against all the caching mechanism on client side. dummy parameter should be replaced with something like "{major version}_{minor_version}_{build_number}_{Revision} which would be unique for each release. – Tohid Nov 10 '14 at 16:58
  • 15
    While this is probably a god solution in a development environment, it isn't fit for production. This will entirely disable the cache **each time the page is loaded** for the file. Imagine 10k page load per day with one 50Kb file, it represents 500Mb of Javascript file on a daily basis. – PhilDulac Jun 06 '16 at 12:16
  • @PhilDulac you could change it from Ticks to return the string value of the day for instance, or the month, or the week of the month. Ultimately it's just showing you how to use the ?v approach – alex Jun 15 '16 at 15:16
  • 3
    @alex Indeed. I just wanted to warn that if the usage demonstrated in the answer makes its way to production, it can have impacts that don't show in development. – PhilDulac Jun 15 '16 at 16:02
  • 2
    A possible way to ensure that new copies get loaded once every day might be to use ''. So it's load once at beginning of day, then cached. – Robb Sadler Sep 13 '17 at 15:17
7

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();
    }
    ...
}
Ivan Kochurkin
  • 4,062
  • 7
  • 41
  • 70
6

The common practice nowadays is to generate a content hash code as part of the file name to force the browser especially IE to reload the javascript files or css files.

For example,

vendor.a7561fb0e9a071baadb9.js
main.b746e3eb72875af2caa9.js

It is generally the job for the build tools such as webpack. Here is more details if anyone wants to try out if you are using webpack.

schrodinger's code
  • 1,744
  • 18
  • 16
5

If you're generating the page that links to the JS files a simple solution is appending the file's last modification timestamp to the generated links.

This is very similar to Huppie's answer, but works in version control systems without keyword substitution. It's also better than append the current time, since that would prevent caching even when the file didn't change at all.

  • I like this solution, since it's easiest to maintain. If you update a .js file, that's all you'll need to do. No need to also update any references to the file, since your code will add the last updated timestamp automatically. – NL3294 Jun 13 '16 at 16:44
4

We have been creating a SaaS for users and providing them a script to attach in their website page, and it was not possible to attach a version with the script as user will attach the script to their website for functionalities and i can't force them to change the version each time we update the script

So, we found a way to load the newer version of the script each time user calls the original script

the script link provided to user

<script src="https://thesaasdomain.com/somejsfile.js" data-ut="user_token"></script>

the script file

if($('script[src^="https://thesaasdomain.com/somejsfile.js?"]').length !== 0) {
   init();
} else {
   loadScript("https://thesaasdomain.com/somejsfile.js?" + guid());
}

var loadscript = function(scriptURL) {
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = scriptURL;
   head.appendChild(script);
}

var guid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

var init = function() {
    // our main code
}

Explanation:

The user have attached the script provided to them in their website and we checked for the unique token attached with the script exists or not using jQuery selector and if not then load it dynamically with newer token (or version)

This is call the same script twice which could be a performance issue, but it really solves the problem of forcing the script to not load from the cache without putting the version in the actual script link given to the user or client

Disclaimer: Do not use if performance is a big issue in your case.

Aman Singh
  • 673
  • 7
  • 20
3

In PHP:

function latest_version($file_name){
    echo $file_name."?".filemtime($_SERVER['DOCUMENT_ROOT'] .$file_name);
}

In HTML:

<script type="text/javascript" src="<?php latest_version('/a-o/javascript/almanacka.js'); ?>">< /script>

How it works:

In HTML, write the filepath and name as you wold do, but in the function only. PHP gets the filetime of the file and returns the filepath+name+"?"+time of latest change

Naman
  • 1,405
  • 15
  • 30
3

The jQuery function getScript can also be used to ensure that a js file is indeed loaded every time the page is loaded.

This is how I did it:

$(document).ready(function(){
    $.getScript("../data/playlist.js", function(data, textStatus, jqxhr){
         startProgram();
    });
});

Check the function at http://api.jquery.com/jQuery.getScript/

By default, $.getScript() sets the cache setting to false. This appends a timestamped query parameter to the request URL to ensure that the browser downloads the script each time it is requested.

Michael Franz
  • 193
  • 1
  • 6
2

My colleague just found a reference to that method right after I posted (in reference to css) at http://www.stefanhayden.com/blog/2006/04/03/css-caching-hack/. Good to see that others are using it and it seems to work. I assume at this point that there isn't a better way than find-replace to increment these "version numbers" in all of the script tags?

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
AdamB
  • 7,780
  • 4
  • 19
  • 13
2

In asp.net mvc you can use @DateTime.UtcNow.ToString() for js file version number. Version number auto change with date and you force clients browser to refresh automatically js file. I using this method and this is work well.

<script src="~/JsFilePath/JsFile.js?v=@DateTime.UtcNow.ToString()"></script>
dragonal
  • 54
  • 4
  • As with other suggested solutions, this will cause the file never to be cached, which is usually undesirable. As long as no changes have been made to the file you probably want the client to use the cached version rather than download the unchanged file again every time. – Philip Stratford May 23 '20 at 14:31
  • You can use below code for your reason, cache file with version number – dragonal May 27 '20 at 06:23
1

Athough it is framework specific, Django 1.4 has this functionailty which works in a similar fashion to the link to the 'greenfelt' site in the above answer

Community
  • 1
  • 1
Trent
  • 2,176
  • 2
  • 31
  • 47
1

Use a version GET variable to prevent browser caching.

Appending ?v=AUTO_INCREMENT_VERSION to the end of your url prevents browser caching - avoiding any and all cached scripts.

Derek Adair
  • 20,298
  • 31
  • 92
  • 133
1

Cache Busting in ASP.NET Core via a tag helper will handle this for you and allow your browser to keep cached scripts/css until the file changes. Simply add the tag helper asp-append-version="true" to your script (js) or link (css) tag:

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true"/>

Dave Paquette has a good example and explanation of cache busting here (bottom of page) Cache Busting

ccherwin
  • 141
  • 1
  • 4
  • Does this not work in regular ASP.NET? I tried adding the asp-append-version to my script tag and all the browser sees is the script tag exactly as it appears in the source, including the asp-append-version attribute. – tolsen64 Jul 26 '17 at 20:03
  • This is a .NET Core attribute associated with Tag Helpers. It appends the script name with a version so that the server/browser always sees the latest version and downloads – ccherwin Jul 31 '17 at 22:42
1

location.reload(true);

see https://www.w3schools.com/jsref/met_loc_reload.asp

I dynamically call this line of code in order to ensure that javascript has been re-retrieved from the web server instead of from the browser's cache in order to escape this problem.

Megan
  • 123
  • 6
  • Adding the `onload="location.reload();"` to my form allows me to get the new JS after a refresh instead of relaunching my page. This a far more elegant solution. Thanks! – ZX9 Nov 06 '19 at 14:37
  • Thanks, could use this with a check if the ip is recognised but hasn't been used to login since the last update perform this on the index page after users initial login. – Fi Horan Nov 21 '19 at 08:25
  • onload="location.reload(true);" The above did not work for me (using flask and current version of Chrome) also: https://www.w3schools.com/jsref/met_loc_reload.asp – CodingMatters Dec 30 '19 at 23:20
1

One solution is to append a query string with a timestamp in it to the URL when fetching the resource. This takes advantage of the fact that a browser will not cache resources fetched from URLs with query strings in them.

You probably don't want the browser not to cache these resources at all though; it's more likely that you want them cached, but you want the browser to fetch a new version of the file when it is made available.

The most common solution seems to be to embed a timestamp or revision number in the file name itself. This is a little more work, because your code needs to be modified to request the correct files, but it means that, e.g. version 7 of your snazzy_javascript_file.js (i.e. snazzy_javascript_file_7.js) is cached on the browser until you release version 8, and then your code changes to fetch snazzy_javascript_file_8.js instead.

Richard Turner
  • 11,186
  • 5
  • 31
  • 37
1

The advantage of using a file.js?V=1 over a fileV1.js is that you do not need to store multiple versions of the JavaScript files on the server.

The trouble I see with file.js?V=1 is that you may have dependant code in another JavaScript file that breaks when using the new version of the library utilities.

For the sake of backwards compatibility, I think it is much better to use jQuery.1.3.js for your new pages and let existing pages use jQuery.1.1.js, until you are ready to upgrade the older pages, if necessary.

0

One simple way. Edit htaccess

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} \.(jpe?g|bmp|png|gif|css|js|mp3|ogg)$ [NC]
RewriteCond %{QUERY_STRING} !^(.+?&v33|)v=33[^&]*(?:&(.*)|)$ [NC]
RewriteRule ^ %{REQUEST_URI}?v=33 [R=301,L]
Goran Siriev
  • 545
  • 5
  • 14
0

You can add file version to your file name so it will be like:

https://www.example.com/script_fv25.js

fv25 => file version nr. 25

And in your .htaccess put this block which will delete the version part from link:

RewriteEngine On
RewriteRule (.*)_fv\d+\.(js|css|txt|jpe?g|png|svg|ico|gif) $1.$2 [L]

so the final link will be:

https://www.example.com/script.js
Mohamad Hamouday
  • 1,024
  • 14
  • 17
0

FRONT-END OPTION

I made this code specifically for those who can't change any settings on the backend. In this case the best way to prevent a very long cache is with:

new Date().getTime()

However, for most programmers the cache can be a few minutes or hours so the simple code above ends up forcing all users to download "the each page browsed". To specify how long this item will remain without reloading I made this code and left several examples below:

// cache-expires-after.js v1
function cacheExpiresAfter(delay = 1, prefix = '', suffix = '') { // seconds
    let now = new Date().getTime().toString();
    now = now.substring(now.length - 11, 10); // remove decades and milliseconds
    now = parseInt(now / delay).toString();
    return prefix + now + suffix;
};

// examples (of the delay argument):
// the value changes every 1 second
var cache = cacheExpiresAfter(1);
// see the sync
setInterval(function(){
    console.log(cacheExpiresAfter(1), new Date().getSeconds() + 's');
}, 1000);

// the value changes every 1 minute
var cache = cacheExpiresAfter(60);
// see the sync
setInterval(function(){
    console.log(cacheExpiresAfter(60), new Date().getMinutes() + 'm:' + new Date().getSeconds() + 's');
}, 1000);

// the value changes every 5 minutes
var cache = cacheExpiresAfter(60 * 5); // OR 300

// the value changes every 1 hour
var cache = cacheExpiresAfter(60 * 60); // OR 3600

// the value changes every 3 hours
var cache = cacheExpiresAfter(60 * 60 * 3); // OR 10800

// the value changes every 1 day
var cache = cacheExpiresAfter(60 * 60 * 24); // OR 86400

// usage example:
let head = (document.head || document.getElementsByTagName('head')[0]);
let script = document.createElement('script');
script.setAttribute('src', '//unpkg.com/sweetalert@2.1.2/dist/sweetalert.min.js' + cacheExpiresAfter(60 * 5, '?'));
head.append(script);

// this works?
let waitSwal = setInterval(function() {
    if (window.swal) {
        clearInterval(waitSwal);
        swal('Script successfully injected', script.outerHTML);
    };
}, 100);
Luis Lobo
  • 33
  • 4
0

A simple trick that works fine for me to prevent conflicts between older and newer javascript files. That means: If there is a conflict and some error occurs, the user will be prompted to press Ctrl-F5.

At the top of the page add something like

<h1 id="welcome"> Welcome to this page <span style="color:red">... press Ctrl-F5</span></h1>

looking like

enter image description here

Let this line of javascript be the last to be executed when loading the page:

document.getElementById("welcome").innerHTML = "Welcome to this page"

In case that no error occurs the welcome greeting above will hardly be visible and almost immediately be replaced by

enter image description here

-1

Below worked for me:

<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
</head>
Mikev
  • 1,844
  • 1
  • 11
  • 22
H.Ostwal
  • 155
  • 4
  • 11
-1

If you are using PHP and Javascript then the following should work for you especially in the situation where you are doing multiple times changes on the file. So, every time you cannot change its version. So, the idea is to create a random number in PHP and then assign it as a version of the JS file.

$fileVersion = rand();
<script src="addNewStudent.js?v=<?php echo $fileVersion; ?>"></script>
Darshan Jain
  • 471
  • 6
  • 15
-1

Simplest solution? Don't let the browser cache at all. Append the current time (in ms) as a query.

(You are still in beta, so you could make a reasonable case for not optimizing for performance. But YMMV here.)

pcorcoran
  • 7,146
  • 6
  • 26
  • 26
  • 14
    IMHO this is a poor solution. What if you are not in BETA and you push out an important update? – d-_-b Aug 24 '10 at 07:42