ChatGPT解决这个技术问题 Extra ChatGPT

How can I force clients to refresh JavaScript files?

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?

I have tried all "answers" below which are actually hacks. None of them can delete the exact image from the cache. No wonder that this question got exactly 666 Likes (2022-05-28). – Obviously a browser feature that is still missing. – Server-site no-cache headers seem to be the only way.
Has anybody tried this?

M
Marco Bonelli

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.


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.
I always knew the query strings are key-value pair as in ?ver=123. Thanks! :)
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.
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.
@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.
C
Chase Seibert

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

can you please be more specific?
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
where do you configure this?
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.
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: agiletribe.wordpress.com/2018/01/29/caching-for-chrome
H
Hắc Huyền Minh

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
@Hắc Huyền Minh: But when the script shall be reloaded, it should not be reloaded from the proxy-cache...
s
sk8terboi87 ツ

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.


This solution is really good, as long as you remember to update the manifest file :)
Please read the documentation as this feature has been removed from the Web standards developer.mozilla.org/en-US/docs/Web/HTML/…
FWIW, I ended up not using this solution. It was much easier to use/maintain the ?<version> approach.
Now it's Deprecated!
E
Erik Corona

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?


This uses PHP tags and if one uses PHP, it is indeed a good idea.
I think adding the changedate would be better than the filesize :)
My initial thought is to add a hash of the file instead of version.
I assume that it also works if add a time stamp Unix, right? e.g ‘...file.js?filever=
use filemtime($file) it output the timestamp of the file, with time() you can't use cache as it change every seconds.
A
Aliaksandr Sushkevich

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


Could you please elaborate on what you did in .htaccess with mod_redirect?
A detailed explanation of this method can be found at particletree.com/notebook/…
It would be great if you could include your .htaccess code within your answer for future reference.
Which browsers does not cache files with "?" in it?
s
schrodinger's code

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.


R
Ravi Ram

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.


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.
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 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 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.
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.
I
Ivan Kochurkin

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();
    }
    ...
}

I am using .Ticks (see my answer on this page)
佚名

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.
N
Naman

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


A
Aman Singh

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.


M
Michael Franz

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.


We need to cache files if no changes happen.
L
Lightness Races in Orbit

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?


This seems to work perfectly for both .css and .js files.
d
dragonal

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>

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.
You can use below code for your reason, cache file with version number
R
Richard Turner

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.


佚名

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.


D
Derek Adair

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.


c
ccherwin

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


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.
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
M
Megan

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.


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!
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.
onload="location.reload(true);" The above did not work for me (using flask and current version of Chrome) also: w3schools.com/jsref/met_loc_reload.asp
T
Trent

Athough it is framework specific, Django 1.4 has the staticfiles app functionality which works in a similar fashion to the 'greenfelt' site in the above answer


G
Goran Siriev

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]

This causes a redirect which is performance-wise a suboptimal, but working solution.
M
Mohamad Hamouday

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

D
Darshan Jain

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>

L
Luis Lobo

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);

p
pcorcoran

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


IMHO this is a poor solution. What if you are not in BETA and you push out an important update?
M
Mikev

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>

H
Hans-Peter Stricker

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

https://i.stack.imgur.com/yiqlQ.png

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

https://i.stack.imgur.com/IVakd.png


oh no please don't do this