AdamB
AdamB

Reputation: 9110

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?

Upvotes: 720

Views: 637904

Answers (30)

Ruhollah Majdoddin
Ruhollah Majdoddin

Reputation: 39

For js resources, use jquery's getScript(). It loads a JavaScript file from the server using a GET HTTP request, then execute it. It sets the cache setting to false, by default. See the docs.

If you have multiple js resources, and need a defer loading, use its callback:

<script src="/js/jquery.min.js"></script>
<script>
  $.getScript('script1.js?').done(function() {
      // Load script2.js only after script1.js is done executing
      $.getScript('script2.js?');
    });
</script>

Upvotes: 0

Martin Baxter
Martin Baxter

Reputation: 239

The suggestion about setting the Cache-Control in HTTP header worked very well for me. Here are the steps you need to take to implement this in Apache 2.4.

Firstly enable the headers module, (in Linux) by running a2enmod headers or (in Windows) editing the Apache config file (eg C:\Apache24\conf\httpd.conf) and uncommenting the line

LoadModule headers_module modules/mod_headers.so

Suppose, as an example, we want to add a one-day cache limit for all .js and .css files. To do this, we edit our site's Apache config file and add code such as:

# Client-side caching to last one-day for .js and .css files
<FilesMatch "\.(js|css)$">
    Header set Cache-Control "max-age=86400, must-revalidate"
</FilesMatch>

The limit of one day is converted into seconds (86,400).

Suppose further, that we have a sub-directory called mystatic which has large and unchanging JavaScript files. We might want to disable the time limit for those files for client performance reasons. To do this, we would add some more lines in the Apache config file:

<Directory /PATHTOWWW/mystatic/>
    <FilesMatch "\.(js)$">
        Header unset Cache-Control
    </FilesMatch>
</Directory>

This switches off the Cache-Control header for .js files in the directory mystatic. (Note that 'PATHTOWWW' should be replaced with the actual path to that directory.)

Upvotes: 0

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 have stuff_*.js to go stuff.js

Upvotes: 22

Reggie Pinkham
Reggie Pinkham

Reputation: 12748

<script>  
  var version = new Date().getTime();  
  var script = document.createElement("script");  
  script.src = "app.js?=" + version;  
  document.body.appendChild(script);  
</script>

Feel free to delete this if someone's already posted it somewhere in the plethora of answers above.

Upvotes: 3

Ingo
Ingo

Reputation: 5381

You can do this with .htaccess

Add into your .htaccess file the following lines:

# DISABLE CACHING
<IfModule mod_headers.c>
  <FilesMatch "\.js$">
      Header set Cache-Control "no-store, max-age=0"
  </FilesMatch>
</IfModule>

Upvotes: -1

Luis Lobo
Luis Lobo

Reputation: 859

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

Upvotes: 1

Trent Gm
Trent Gm

Reputation: 2487

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

Upvotes: 2

Darshan Jain
Darshan Jain

Reputation: 838

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>

Upvotes: 0

Mohamad Hamouday
Mohamad Hamouday

Reputation: 2793

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

Upvotes: 2

Megan
Megan

Reputation: 83

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.

Upvotes: 2

H.Ostwal
H.Ostwal

Reputation: 341

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>

Upvotes: 0

dragonal
dragonal

Reputation: 92

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>

Upvotes: 4

amos
amos

Reputation: 5402

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.

Upvotes: 37

schrodinger&#39;s code
schrodinger&#39;s code

Reputation: 2734

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.

Upvotes: 14

Aman Singh
Aman Singh

Reputation: 753

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.

Upvotes: 6

ccherwin
ccherwin

Reputation: 191

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

Upvotes: 2

Goran Siriev
Goran Siriev

Reputation: 703

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]

Upvotes: 1

user1944129
user1944129

Reputation: 71

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

Upvotes: 7

Erik Corona
Erik Corona

Reputation: 488

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?

Upvotes: 38

Michael Franz
Michael Franz

Reputation: 203

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.

Upvotes: 4

Huppie
Huppie

Reputation: 11432

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.

Upvotes: 623

Ravi Ram
Ravi Ram

Reputation: 24488

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.

Upvotes: 12

Ivan Kochurkin
Ivan Kochurkin

Reputation: 4501

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

Upvotes: 8

AdamB
AdamB

Reputation: 9110

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?

Upvotes: 3

Hắc Huyền Minh
Hắc Huyền Minh

Reputation: 1035

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.

Upvotes: 46

Derek Adair
Derek Adair

Reputation: 21935

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.

Upvotes: 2

Willabee
Willabee

Reputation:

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.

Upvotes: 2

Chase Seibert
Chase Seibert

Reputation: 15851

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

Upvotes: 113

Pazu
Pazu

Reputation:

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.

Upvotes: 6

pcorcoran
pcorcoran

Reputation: 8082

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

Upvotes: 0

Related Questions