What are best practices for preventing stale CSS and JavaScript - javascript

I'm researching this for a project and I'm wondering what other people are doing to prevent stale CSS and JavaScript files from being served with each new release. I don't want to append a timestamp or something similar which may prevent caching on every request.
I'm working with the Spring 2.5 MVC framework and I'm already using the google api's to serve prototype and scriptaculous. I'm also considering using Amazon S3 and the new Cloudfront offering to minimize network latency.

I add a parameter to the request with the revision number, something like:
<script type="text/javascript" src="/path/to/script.js?ver=456"></script>
The 'ver' parameter is updated automatically with each build (read from file, which the build updates). This makes sure the scripts are cached only for the current revision.

Like #eran-galperin, I use a parameter in the reference to the JS file, but I include a server-generated reference to the file's "last modified" date. #stein-g-strindhaug suggests this approach. It would look something like this:
<script type="text/javascript" src="/path/to/script.js?1347486578"></script>
The server ignores the parameter for the static file and the client may cache the script until the date code changes. If (and only if) you modify the JS file on the server, the date code will change automatically.
For instance, in PHP, my script to create this code looks like this:
function cachePreventCode($filename) {
if (!file_exists($filename))
return "";
$mtime = filemtime($filename);
return $mtime;
}
So then when your PHP file includes a reference to a CSS file, it might look like this:
<link rel="stylesheet" type="text/css" href="main.css?<?= cachePreventCode("main.css") ?>" />
... which will create ...
<link rel="stylesheet" type="text/css" href="main.css?1347489244" />

With regards to cached files, I have yet to run into any issues of bugs related to stale cached files by using the querystring method.
However, with regards to performance, and echoing Todd B's mention of revving by filename, please check out Steve Souders' work for more on the topic:
"Squid, a popular proxy, doesn’t cache resources with a querystring. This hurts performance when multiple users behind a proxy cache request the same file - rather than using the cached version everybody would have to send a request to the origin server."
"Proxy administrators can change the configuration to support caching resources with a querystring, when the caching headers indicate that is appropriate. But the default configuration is what web developers should expect to encounter most frequently."
http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/

Use a conditional get request with an If-Modified-Since header

This is actually a very hard issue, and something that you can spend a while engineering the correct solution for.
I would recommend publishing your files using a timestamp and/or version built into the url, so instead of:
/media/js/my.js you end up with:
/media/js/v12/my.js or something similar.
You can automate the versioning/timestamping with any tool.
This has the added benefit of NOT breaking the site as you roll out new versions, and lets you do real side-by-side testing (unlike a rewrite rule that just strips the version and sends back the newest file).
One thing to watch out for with JS or CSS is when you include dependent urls inside of them (background images, etc) you need to make sure the JS/CSS timestamp/version changes if a resource inside does (as well as rewrite them, but that is possible with a very simple regex and a resource manifest).
No matter what you do make sure not to toss a ?vblah on the end, as you are basically throwing caching out the window when you do that (which is unfortunate, as it is by far the easiest way to handle this)

If you get the "modified time" of the file as a timestamp it will be cached until the file is modified. Just use a helper function (or whatever it is called in other frameworks) to add script/css/image tags that get the timestamp from the file. On a unix like system (wich most survers are) you could simply touch the files to force the modified time to change if necessary.
Ruby on Rails uses this strategy in production mode (by default I beleave), and uses a normal timestamp in development mode (to be really sure something isn't cached).

If you use MAVEN, you can use this, ADD on you pom.xml:
<properties>
<maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
</properties>
With this you can acess ${timestamp} in your view.
Like this sample:
<script type="text/javascript" src="/js/myScript.js?t=${timestamp}"></script>

Based on Todd Berman's answer of incorporating a revision number into the URL (but not as a query string), a perhaps slightly more convenient approach would be to have the server transform the versioned URL into a canonical form. This could be done with symlinks, e.g.:
/media/js/v12/my.js => /media/js/my.js
or you could set up server-side URL rewrites to always transform paths of the form /media/js/v*/my.js to, say, /media/js/my.js.

Related

Force browsers to download latest versions of updated files [duplicate]

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.
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 have found John Millikin's 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 that 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.)
This solution is written in PHP, but it should be easily adapted to other languages.
The original .htaccess regex can cause problems with files like json-1.3.js. The 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.
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 information 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.
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, Ruby on Rails, Python, static HTML -- anything) and rewrites links to CSS, JavaScript, 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.
Instead of changing the version manually, I would recommend you use an MD5 hash of the actual CSS file.
So your URL would be something like
http://mysite.com/css/[md5_hash_here]/style.css
You could still use the rewrite rule to strip out the hash, but the advantage is that now you can set your cache policy to "cache forever", since if the URL is the same, that means that the file is unchanged.
You can then write a simple shell script that would compute the hash of the file and update your tag (you'd probably want to move it to a separate file for inclusion).
Simply run that script every time CSS changes and you're good. The browser will ONLY reload your files when they are altered. If you make an edit and then undo it, there's no pain in figuring out which version you need to return to in order for your visitors not to re-download.
I am not sure why you guys/gals 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 href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">
filemtime() is a PHP function that returns the file modified timestamp.
You can just put ?foo=1234 at the end of your CSS / JavaScript import, changing 1234 to be whatever you like. Have a look at the Stack Overflow HTML source for an example.
The idea there being that the ? parameters are discarded / ignored on the request anyway and you can change that number when you roll out a new version.
Note: There is some argument with regard to exactly how this affects caching. I believe the general gist of it is that GET requests, with or without parameters should be cachable, so the above solution should work.
However, it is down to both the web server to decide if it wants to adhere to that part of the spec and the browser the user uses, as it can just go right ahead and ask for a fresh version anyway.
I've heard this called "auto versioning". The most common method is to include the static file's modification time somewhere in the URL, and strip it out using rewrite handlers or URL configurations:
See also:
Automatic asset versioning in Django
Automatically Version Your CSS and JavaScript Files
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 rethink 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:
Your CD pipeline deploys the new version N of the application onto the server
You navigate within the SPA, which sends an XMLHttpRequest (XHR) to the server to get /some.template
(Your browser hasn't refreshed the page, so you're still running version M)
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 applications run into this issue when two conditions are met:
Resources are requested asynchronously some time after the initial page load
The application logic assumes things (that may change in future versions) about resource content
Once your application needs to serve up multiple versions in parallel, solving caching and "reloading" becomes trivial:
Install all site files into versioned directories: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
Set HTTP headers to let browsers cache files forever
(Or better yet, put everything in a CDN)
Update all <script> and <link> tags, etc. to point to that file in one of the versioned directories
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.
In Laravel (PHP) we can do it in the 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'))}}">
Example HTML output (filemtime return time as as a Unix timestamp)
<link rel="stylesheet" href="assets/css/your.css?v=1577772366">
Don’t use foo.css?version=1!
Browsers aren't supposed to cache URLs with GET variables. According to http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, though Internet Explorer and Firefox ignore this, Opera and Safari don't! Instead, use foo.v1234.css, and use rewrite rules to strip out the version number.
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.
The RewriteRule needs a small update for JavaScript 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]
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?
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 my development, I find that Chrome has a great solution.
https://superuser.com/a/512833
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 lightweight way to get what you want!
Thanks to 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.
/**
* 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;
}
}
}
}
I have not found the client-side 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>
Say you have a file available at:
/styles/screen.css
You can either append a query parameter with version information onto the URI, e.g.:
/styles/screen.css?v=1234
Or you can prepend version information, e.g.:
/v/1234/styles/screen.css
IMHO, the second method is better for CSS files, because they can refer to images using relative URLs which means that if you specify a background-image like so:
body {
background-image: url('images/happy.gif');
}
Its URL will effectively be:
/v/1234/styles/images/happy.gif
This means that if you update the version number used, the server will treat this as a new resource and not use a cached version. If you base your version number on the Subversion, CVS, etc. revision this means that changes to images referenced in CSS files will be noticed. That isn't guaranteed with the first scheme, i.e. the URL images/happy.gif relative to /styles/screen.css?v=1235 is /styles/images/happy.gif which doesn't contain any version information.
I have implemented a caching solution using this technique with Java servlets and simply handle requests to /v/* with a servlet that delegates to the underlying resource (i.e. /styles/screen.css). In development mode I set caching headers that tell the client to always check the freshness of the resource with the server (this typically results in a 304 if you delegate to Tomcat's DefaultServlet and the .css, .js, etc. file hasn't changed) while in deployment mode I set headers that say "cache forever".
You could simply add some random number with the CSS and JavaScript URL like
example.css?randomNo = Math.random()
Google Chrome has the Hard Reload as well as the Empty Cache and Hard Reload option. You can click and hold the reload button (in Inspect Mode) to select one.
I recently solved this using Python. Here is the code (it should be easy to adopt to other languages):
def import_tag(pattern, name, **kw):
if name[0] == "/":
name = name[1:]
# Additional HTML attributes
attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
try:
# Get the files modification time
mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
include = "%s?%d" % (name, mtime)
# This is the same as sprintf(pattern, attrs, include) in other
# languages
return pattern % (attrs, include)
except:
# In case of error return the include without the added query
# parameter.
return pattern % (attrs, name)
def script(name, **kw):
return import_tag('<script %s src="/%s"></script>', name, **kw)
def stylesheet(name, **kw):
return import_tag('<link rel="stylesheet" type="text/css" %s href="/%s">', name, **kw)
This code basically appends the files time-stamp as a query parameter to the URL. The call of the following function
script("/main.css")
will result in
<link rel="stylesheet" type="text/css" href="/main.css?1221842734">
The advantage of course is that you do never have to change your HTML content again, touching the CSS file will automatically trigger a cache invalidation. It works very well and the overhead is not noticeable.
You can force a "session-wide caching" if you add the session-id as a spurious parameter of the JavaScript/CSS file:
<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>
If you want a version-wide caching, you could add some code to print the file date or similar. If you're using Java you can use a custom-tag to generate the link in an elegant way.
<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
For ASP.NET I propose the following solution with advanced options (debug/release mode, versions):
Include JavaScript or CSS files this 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 are 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();
}
...
}
If you're using Git and PHP, you can reload the script from the cache each time there is a change in the Git repository, 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;
Simply add this code where you want to do a hard reload (force the browser to reload cached CSS and JavaScript files):
$(window).load(function() {
location.reload(true);
});
Do this inside the .load, so it does not refresh like a loop.
For development: use a browser setting: for example, Chrome network tab has a disable cache option.
For production: append a unique query parameter to the request (for example, q?Date.now()) with a server-side rendering framework or pure JavaScript code.
// Pure JavaScript unique query parameter generation
//
//=== myfile.js
function hello() { console.log('hello') };
//=== end of file
<script type="text/javascript">
document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
// document.write is considered bad practice!
// We can't use hello() yet
</script>')
<script type="text/javascript">
hello();
</script>
For developers with this problem while developing and testing:
Remove caching briefly.
"keep caching consistent with the file" .. it's way too much hassle ..
Generally speaking, I don't mind loading more - even loading again files which did not change - on most projects - is practically irrelevant. While developing an application - we are mostly loading from disk, on localhost:port - so this increase in network traffic issue is not a deal breaking issue.
Most small projects are just playing around - they never end-up in production. So for them you don't need anything more...
As such if you use Chrome DevTools, you can follow this disable-caching approach like in the image below:
And if you have Firefox caching issues:
Do this only in development. You also need a mechanism to force reload for production, since your users will use old cache invalidated modules if you update your application frequently and you don't provide a dedicated cache synchronisation mechanism like the ones described in the answers above.
Yes, this information is already in previous answers, but I still needed to do a Google search to find it.
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 web server's response, in particular the HTTP headers - for how long is this resource valid? Was this resource updated since I last retrieved it? etc.
If things are configured 'correctly', just updating the files of your application should (at some point) refresh the browser's 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 in How Web Caches Work.
Just use server-side code to add the date of the file... that way it will be cached and only reloaded when the file changes.
In ASP.NET:
<link rel="stylesheet" href="~/css/custom.css?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />
<script type="text/javascript" src="~/js/custom.js?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>
This can be simplified to:
<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>
By adding an extension method to your project to extend Page:
public static class Extension_Methods
{
public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
{
string sFilePath = oPg.Server.MapPath(sRelPath);
string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");
return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
}
}
You can use SRI to break the browser cache. You only have to update your index.html file with the new SRI hash every time. When the browser loads the HTML and finds out the SRI hash on the HTML page didn't match that of the cached version of the resource, it will reload your resource from your servers. It also comes with a good side effect of bypassing cross-origin read blocking.
<script src="https://jessietessie.github.io/google-translate-token-generator/google_translate_token_generator.js" integrity="sha384-muTMBCWlaLhgTXLmflAEQVaaGwxYe1DYIf2fGdRkaAQeb4Usma/kqRWFWErr2BSi" crossorigin="anonymous"></script>

Force a clear cache [duplicate]

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.
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 have found John Millikin's 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 that 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.)
This solution is written in PHP, but it should be easily adapted to other languages.
The original .htaccess regex can cause problems with files like json-1.3.js. The 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.
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 information 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.
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, Ruby on Rails, Python, static HTML -- anything) and rewrites links to CSS, JavaScript, 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.
Instead of changing the version manually, I would recommend you use an MD5 hash of the actual CSS file.
So your URL would be something like
http://mysite.com/css/[md5_hash_here]/style.css
You could still use the rewrite rule to strip out the hash, but the advantage is that now you can set your cache policy to "cache forever", since if the URL is the same, that means that the file is unchanged.
You can then write a simple shell script that would compute the hash of the file and update your tag (you'd probably want to move it to a separate file for inclusion).
Simply run that script every time CSS changes and you're good. The browser will ONLY reload your files when they are altered. If you make an edit and then undo it, there's no pain in figuring out which version you need to return to in order for your visitors not to re-download.
I am not sure why you guys/gals 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 href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">
filemtime() is a PHP function that returns the file modified timestamp.
You can just put ?foo=1234 at the end of your CSS / JavaScript import, changing 1234 to be whatever you like. Have a look at the Stack Overflow HTML source for an example.
The idea there being that the ? parameters are discarded / ignored on the request anyway and you can change that number when you roll out a new version.
Note: There is some argument with regard to exactly how this affects caching. I believe the general gist of it is that GET requests, with or without parameters should be cachable, so the above solution should work.
However, it is down to both the web server to decide if it wants to adhere to that part of the spec and the browser the user uses, as it can just go right ahead and ask for a fresh version anyway.
I've heard this called "auto versioning". The most common method is to include the static file's modification time somewhere in the URL, and strip it out using rewrite handlers or URL configurations:
See also:
Automatic asset versioning in Django
Automatically Version Your CSS and JavaScript Files
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 rethink 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:
Your CD pipeline deploys the new version N of the application onto the server
You navigate within the SPA, which sends an XMLHttpRequest (XHR) to the server to get /some.template
(Your browser hasn't refreshed the page, so you're still running version M)
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 applications run into this issue when two conditions are met:
Resources are requested asynchronously some time after the initial page load
The application logic assumes things (that may change in future versions) about resource content
Once your application needs to serve up multiple versions in parallel, solving caching and "reloading" becomes trivial:
Install all site files into versioned directories: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
Set HTTP headers to let browsers cache files forever
(Or better yet, put everything in a CDN)
Update all <script> and <link> tags, etc. to point to that file in one of the versioned directories
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.
In Laravel (PHP) we can do it in the 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'))}}">
Example HTML output (filemtime return time as as a Unix timestamp)
<link rel="stylesheet" href="assets/css/your.css?v=1577772366">
Don’t use foo.css?version=1!
Browsers aren't supposed to cache URLs with GET variables. According to http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, though Internet Explorer and Firefox ignore this, Opera and Safari don't! Instead, use foo.v1234.css, and use rewrite rules to strip out the version number.
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.
The RewriteRule needs a small update for JavaScript 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]
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?
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 my development, I find that Chrome has a great solution.
https://superuser.com/a/512833
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 lightweight way to get what you want!
Thanks to 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.
/**
* 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;
}
}
}
}
I have not found the client-side 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>
Say you have a file available at:
/styles/screen.css
You can either append a query parameter with version information onto the URI, e.g.:
/styles/screen.css?v=1234
Or you can prepend version information, e.g.:
/v/1234/styles/screen.css
IMHO, the second method is better for CSS files, because they can refer to images using relative URLs which means that if you specify a background-image like so:
body {
background-image: url('images/happy.gif');
}
Its URL will effectively be:
/v/1234/styles/images/happy.gif
This means that if you update the version number used, the server will treat this as a new resource and not use a cached version. If you base your version number on the Subversion, CVS, etc. revision this means that changes to images referenced in CSS files will be noticed. That isn't guaranteed with the first scheme, i.e. the URL images/happy.gif relative to /styles/screen.css?v=1235 is /styles/images/happy.gif which doesn't contain any version information.
I have implemented a caching solution using this technique with Java servlets and simply handle requests to /v/* with a servlet that delegates to the underlying resource (i.e. /styles/screen.css). In development mode I set caching headers that tell the client to always check the freshness of the resource with the server (this typically results in a 304 if you delegate to Tomcat's DefaultServlet and the .css, .js, etc. file hasn't changed) while in deployment mode I set headers that say "cache forever".
You could simply add some random number with the CSS and JavaScript URL like
example.css?randomNo = Math.random()
Google Chrome has the Hard Reload as well as the Empty Cache and Hard Reload option. You can click and hold the reload button (in Inspect Mode) to select one.
I recently solved this using Python. Here is the code (it should be easy to adopt to other languages):
def import_tag(pattern, name, **kw):
if name[0] == "/":
name = name[1:]
# Additional HTML attributes
attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
try:
# Get the files modification time
mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
include = "%s?%d" % (name, mtime)
# This is the same as sprintf(pattern, attrs, include) in other
# languages
return pattern % (attrs, include)
except:
# In case of error return the include without the added query
# parameter.
return pattern % (attrs, name)
def script(name, **kw):
return import_tag('<script %s src="/%s"></script>', name, **kw)
def stylesheet(name, **kw):
return import_tag('<link rel="stylesheet" type="text/css" %s href="/%s">', name, **kw)
This code basically appends the files time-stamp as a query parameter to the URL. The call of the following function
script("/main.css")
will result in
<link rel="stylesheet" type="text/css" href="/main.css?1221842734">
The advantage of course is that you do never have to change your HTML content again, touching the CSS file will automatically trigger a cache invalidation. It works very well and the overhead is not noticeable.
You can force a "session-wide caching" if you add the session-id as a spurious parameter of the JavaScript/CSS file:
<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>
If you want a version-wide caching, you could add some code to print the file date or similar. If you're using Java you can use a custom-tag to generate the link in an elegant way.
<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
For ASP.NET I propose the following solution with advanced options (debug/release mode, versions):
Include JavaScript or CSS files this 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 are 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();
}
...
}
If you're using Git and PHP, you can reload the script from the cache each time there is a change in the Git repository, 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;
Simply add this code where you want to do a hard reload (force the browser to reload cached CSS and JavaScript files):
$(window).load(function() {
location.reload(true);
});
Do this inside the .load, so it does not refresh like a loop.
For development: use a browser setting: for example, Chrome network tab has a disable cache option.
For production: append a unique query parameter to the request (for example, q?Date.now()) with a server-side rendering framework or pure JavaScript code.
// Pure JavaScript unique query parameter generation
//
//=== myfile.js
function hello() { console.log('hello') };
//=== end of file
<script type="text/javascript">
document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
// document.write is considered bad practice!
// We can't use hello() yet
</script>')
<script type="text/javascript">
hello();
</script>
For developers with this problem while developing and testing:
Remove caching briefly.
"keep caching consistent with the file" .. it's way too much hassle ..
Generally speaking, I don't mind loading more - even loading again files which did not change - on most projects - is practically irrelevant. While developing an application - we are mostly loading from disk, on localhost:port - so this increase in network traffic issue is not a deal breaking issue.
Most small projects are just playing around - they never end-up in production. So for them you don't need anything more...
As such if you use Chrome DevTools, you can follow this disable-caching approach like in the image below:
And if you have Firefox caching issues:
Do this only in development. You also need a mechanism to force reload for production, since your users will use old cache invalidated modules if you update your application frequently and you don't provide a dedicated cache synchronisation mechanism like the ones described in the answers above.
Yes, this information is already in previous answers, but I still needed to do a Google search to find it.
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 web server's response, in particular the HTTP headers - for how long is this resource valid? Was this resource updated since I last retrieved it? etc.
If things are configured 'correctly', just updating the files of your application should (at some point) refresh the browser's 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 in How Web Caches Work.
Just use server-side code to add the date of the file... that way it will be cached and only reloaded when the file changes.
In ASP.NET:
<link rel="stylesheet" href="~/css/custom.css?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />
<script type="text/javascript" src="~/js/custom.js?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>
This can be simplified to:
<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>
By adding an extension method to your project to extend Page:
public static class Extension_Methods
{
public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
{
string sFilePath = oPg.Server.MapPath(sRelPath);
string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");
return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
}
}
You can use SRI to break the browser cache. You only have to update your index.html file with the new SRI hash every time. When the browser loads the HTML and finds out the SRI hash on the HTML page didn't match that of the cached version of the resource, it will reload your resource from your servers. It also comes with a good side effect of bypassing cross-origin read blocking.
<script src="https://jessietessie.github.io/google-translate-token-generator/google_translate_token_generator.js" integrity="sha384-muTMBCWlaLhgTXLmflAEQVaaGwxYe1DYIf2fGdRkaAQeb4Usma/kqRWFWErr2BSi" crossorigin="anonymous"></script>

Code is not updated without hard reload or clear cache [duplicate]

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.
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 have found John Millikin's 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 that 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.)
This solution is written in PHP, but it should be easily adapted to other languages.
The original .htaccess regex can cause problems with files like json-1.3.js. The 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.
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 information 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.
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, Ruby on Rails, Python, static HTML -- anything) and rewrites links to CSS, JavaScript, 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.
Instead of changing the version manually, I would recommend you use an MD5 hash of the actual CSS file.
So your URL would be something like
http://mysite.com/css/[md5_hash_here]/style.css
You could still use the rewrite rule to strip out the hash, but the advantage is that now you can set your cache policy to "cache forever", since if the URL is the same, that means that the file is unchanged.
You can then write a simple shell script that would compute the hash of the file and update your tag (you'd probably want to move it to a separate file for inclusion).
Simply run that script every time CSS changes and you're good. The browser will ONLY reload your files when they are altered. If you make an edit and then undo it, there's no pain in figuring out which version you need to return to in order for your visitors not to re-download.
I am not sure why you guys/gals 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 href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">
filemtime() is a PHP function that returns the file modified timestamp.
You can just put ?foo=1234 at the end of your CSS / JavaScript import, changing 1234 to be whatever you like. Have a look at the Stack Overflow HTML source for an example.
The idea there being that the ? parameters are discarded / ignored on the request anyway and you can change that number when you roll out a new version.
Note: There is some argument with regard to exactly how this affects caching. I believe the general gist of it is that GET requests, with or without parameters should be cachable, so the above solution should work.
However, it is down to both the web server to decide if it wants to adhere to that part of the spec and the browser the user uses, as it can just go right ahead and ask for a fresh version anyway.
I've heard this called "auto versioning". The most common method is to include the static file's modification time somewhere in the URL, and strip it out using rewrite handlers or URL configurations:
See also:
Automatic asset versioning in Django
Automatically Version Your CSS and JavaScript Files
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 rethink 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:
Your CD pipeline deploys the new version N of the application onto the server
You navigate within the SPA, which sends an XMLHttpRequest (XHR) to the server to get /some.template
(Your browser hasn't refreshed the page, so you're still running version M)
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 applications run into this issue when two conditions are met:
Resources are requested asynchronously some time after the initial page load
The application logic assumes things (that may change in future versions) about resource content
Once your application needs to serve up multiple versions in parallel, solving caching and "reloading" becomes trivial:
Install all site files into versioned directories: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
Set HTTP headers to let browsers cache files forever
(Or better yet, put everything in a CDN)
Update all <script> and <link> tags, etc. to point to that file in one of the versioned directories
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.
In Laravel (PHP) we can do it in the 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'))}}">
Example HTML output (filemtime return time as as a Unix timestamp)
<link rel="stylesheet" href="assets/css/your.css?v=1577772366">
Don’t use foo.css?version=1!
Browsers aren't supposed to cache URLs with GET variables. According to http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, though Internet Explorer and Firefox ignore this, Opera and Safari don't! Instead, use foo.v1234.css, and use rewrite rules to strip out the version number.
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.
The RewriteRule needs a small update for JavaScript 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]
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?
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 my development, I find that Chrome has a great solution.
https://superuser.com/a/512833
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 lightweight way to get what you want!
Thanks to 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.
/**
* 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;
}
}
}
}
I have not found the client-side 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>
Say you have a file available at:
/styles/screen.css
You can either append a query parameter with version information onto the URI, e.g.:
/styles/screen.css?v=1234
Or you can prepend version information, e.g.:
/v/1234/styles/screen.css
IMHO, the second method is better for CSS files, because they can refer to images using relative URLs which means that if you specify a background-image like so:
body {
background-image: url('images/happy.gif');
}
Its URL will effectively be:
/v/1234/styles/images/happy.gif
This means that if you update the version number used, the server will treat this as a new resource and not use a cached version. If you base your version number on the Subversion, CVS, etc. revision this means that changes to images referenced in CSS files will be noticed. That isn't guaranteed with the first scheme, i.e. the URL images/happy.gif relative to /styles/screen.css?v=1235 is /styles/images/happy.gif which doesn't contain any version information.
I have implemented a caching solution using this technique with Java servlets and simply handle requests to /v/* with a servlet that delegates to the underlying resource (i.e. /styles/screen.css). In development mode I set caching headers that tell the client to always check the freshness of the resource with the server (this typically results in a 304 if you delegate to Tomcat's DefaultServlet and the .css, .js, etc. file hasn't changed) while in deployment mode I set headers that say "cache forever".
You could simply add some random number with the CSS and JavaScript URL like
example.css?randomNo = Math.random()
Google Chrome has the Hard Reload as well as the Empty Cache and Hard Reload option. You can click and hold the reload button (in Inspect Mode) to select one.
I recently solved this using Python. Here is the code (it should be easy to adopt to other languages):
def import_tag(pattern, name, **kw):
if name[0] == "/":
name = name[1:]
# Additional HTML attributes
attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
try:
# Get the files modification time
mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
include = "%s?%d" % (name, mtime)
# This is the same as sprintf(pattern, attrs, include) in other
# languages
return pattern % (attrs, include)
except:
# In case of error return the include without the added query
# parameter.
return pattern % (attrs, name)
def script(name, **kw):
return import_tag('<script %s src="/%s"></script>', name, **kw)
def stylesheet(name, **kw):
return import_tag('<link rel="stylesheet" type="text/css" %s href="/%s">', name, **kw)
This code basically appends the files time-stamp as a query parameter to the URL. The call of the following function
script("/main.css")
will result in
<link rel="stylesheet" type="text/css" href="/main.css?1221842734">
The advantage of course is that you do never have to change your HTML content again, touching the CSS file will automatically trigger a cache invalidation. It works very well and the overhead is not noticeable.
You can force a "session-wide caching" if you add the session-id as a spurious parameter of the JavaScript/CSS file:
<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>
If you want a version-wide caching, you could add some code to print the file date or similar. If you're using Java you can use a custom-tag to generate the link in an elegant way.
<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
For ASP.NET I propose the following solution with advanced options (debug/release mode, versions):
Include JavaScript or CSS files this 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 are 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();
}
...
}
If you're using Git and PHP, you can reload the script from the cache each time there is a change in the Git repository, 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;
Simply add this code where you want to do a hard reload (force the browser to reload cached CSS and JavaScript files):
$(window).load(function() {
location.reload(true);
});
Do this inside the .load, so it does not refresh like a loop.
For development: use a browser setting: for example, Chrome network tab has a disable cache option.
For production: append a unique query parameter to the request (for example, q?Date.now()) with a server-side rendering framework or pure JavaScript code.
// Pure JavaScript unique query parameter generation
//
//=== myfile.js
function hello() { console.log('hello') };
//=== end of file
<script type="text/javascript">
document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
// document.write is considered bad practice!
// We can't use hello() yet
</script>')
<script type="text/javascript">
hello();
</script>
For developers with this problem while developing and testing:
Remove caching briefly.
"keep caching consistent with the file" .. it's way too much hassle ..
Generally speaking, I don't mind loading more - even loading again files which did not change - on most projects - is practically irrelevant. While developing an application - we are mostly loading from disk, on localhost:port - so this increase in network traffic issue is not a deal breaking issue.
Most small projects are just playing around - they never end-up in production. So for them you don't need anything more...
As such if you use Chrome DevTools, you can follow this disable-caching approach like in the image below:
And if you have Firefox caching issues:
Do this only in development. You also need a mechanism to force reload for production, since your users will use old cache invalidated modules if you update your application frequently and you don't provide a dedicated cache synchronisation mechanism like the ones described in the answers above.
Yes, this information is already in previous answers, but I still needed to do a Google search to find it.
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 web server's response, in particular the HTTP headers - for how long is this resource valid? Was this resource updated since I last retrieved it? etc.
If things are configured 'correctly', just updating the files of your application should (at some point) refresh the browser's 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 in How Web Caches Work.
Just use server-side code to add the date of the file... that way it will be cached and only reloaded when the file changes.
In ASP.NET:
<link rel="stylesheet" href="~/css/custom.css?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />
<script type="text/javascript" src="~/js/custom.js?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>
This can be simplified to:
<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>
By adding an extension method to your project to extend Page:
public static class Extension_Methods
{
public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
{
string sFilePath = oPg.Server.MapPath(sRelPath);
string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");
return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
}
}
You can use SRI to break the browser cache. You only have to update your index.html file with the new SRI hash every time. When the browser loads the HTML and finds out the SRI hash on the HTML page didn't match that of the cached version of the resource, it will reload your resource from your servers. It also comes with a good side effect of bypassing cross-origin read blocking.
<script src="https://jessietessie.github.io/google-translate-token-generator/google_translate_token_generator.js" integrity="sha384-muTMBCWlaLhgTXLmflAEQVaaGwxYe1DYIf2fGdRkaAQeb4Usma/kqRWFWErr2BSi" crossorigin="anonymous"></script>

Angular JS 1 - cache issue [duplicate]

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.
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 have found John Millikin's 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 that 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.)
This solution is written in PHP, but it should be easily adapted to other languages.
The original .htaccess regex can cause problems with files like json-1.3.js. The 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.
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 information 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.
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, Ruby on Rails, Python, static HTML -- anything) and rewrites links to CSS, JavaScript, 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.
Instead of changing the version manually, I would recommend you use an MD5 hash of the actual CSS file.
So your URL would be something like
http://mysite.com/css/[md5_hash_here]/style.css
You could still use the rewrite rule to strip out the hash, but the advantage is that now you can set your cache policy to "cache forever", since if the URL is the same, that means that the file is unchanged.
You can then write a simple shell script that would compute the hash of the file and update your tag (you'd probably want to move it to a separate file for inclusion).
Simply run that script every time CSS changes and you're good. The browser will ONLY reload your files when they are altered. If you make an edit and then undo it, there's no pain in figuring out which version you need to return to in order for your visitors not to re-download.
I am not sure why you guys/gals 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 href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">
filemtime() is a PHP function that returns the file modified timestamp.
You can just put ?foo=1234 at the end of your CSS / JavaScript import, changing 1234 to be whatever you like. Have a look at the Stack Overflow HTML source for an example.
The idea there being that the ? parameters are discarded / ignored on the request anyway and you can change that number when you roll out a new version.
Note: There is some argument with regard to exactly how this affects caching. I believe the general gist of it is that GET requests, with or without parameters should be cachable, so the above solution should work.
However, it is down to both the web server to decide if it wants to adhere to that part of the spec and the browser the user uses, as it can just go right ahead and ask for a fresh version anyway.
I've heard this called "auto versioning". The most common method is to include the static file's modification time somewhere in the URL, and strip it out using rewrite handlers or URL configurations:
See also:
Automatic asset versioning in Django
Automatically Version Your CSS and JavaScript Files
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 rethink 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:
Your CD pipeline deploys the new version N of the application onto the server
You navigate within the SPA, which sends an XMLHttpRequest (XHR) to the server to get /some.template
(Your browser hasn't refreshed the page, so you're still running version M)
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 applications run into this issue when two conditions are met:
Resources are requested asynchronously some time after the initial page load
The application logic assumes things (that may change in future versions) about resource content
Once your application needs to serve up multiple versions in parallel, solving caching and "reloading" becomes trivial:
Install all site files into versioned directories: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
Set HTTP headers to let browsers cache files forever
(Or better yet, put everything in a CDN)
Update all <script> and <link> tags, etc. to point to that file in one of the versioned directories
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.
In Laravel (PHP) we can do it in the 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'))}}">
Example HTML output (filemtime return time as as a Unix timestamp)
<link rel="stylesheet" href="assets/css/your.css?v=1577772366">
Don’t use foo.css?version=1!
Browsers aren't supposed to cache URLs with GET variables. According to http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, though Internet Explorer and Firefox ignore this, Opera and Safari don't! Instead, use foo.v1234.css, and use rewrite rules to strip out the version number.
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.
The RewriteRule needs a small update for JavaScript 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]
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?
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 my development, I find that Chrome has a great solution.
https://superuser.com/a/512833
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 lightweight way to get what you want!
Thanks to 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.
/**
* 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;
}
}
}
}
I have not found the client-side 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>
Say you have a file available at:
/styles/screen.css
You can either append a query parameter with version information onto the URI, e.g.:
/styles/screen.css?v=1234
Or you can prepend version information, e.g.:
/v/1234/styles/screen.css
IMHO, the second method is better for CSS files, because they can refer to images using relative URLs which means that if you specify a background-image like so:
body {
background-image: url('images/happy.gif');
}
Its URL will effectively be:
/v/1234/styles/images/happy.gif
This means that if you update the version number used, the server will treat this as a new resource and not use a cached version. If you base your version number on the Subversion, CVS, etc. revision this means that changes to images referenced in CSS files will be noticed. That isn't guaranteed with the first scheme, i.e. the URL images/happy.gif relative to /styles/screen.css?v=1235 is /styles/images/happy.gif which doesn't contain any version information.
I have implemented a caching solution using this technique with Java servlets and simply handle requests to /v/* with a servlet that delegates to the underlying resource (i.e. /styles/screen.css). In development mode I set caching headers that tell the client to always check the freshness of the resource with the server (this typically results in a 304 if you delegate to Tomcat's DefaultServlet and the .css, .js, etc. file hasn't changed) while in deployment mode I set headers that say "cache forever".
You could simply add some random number with the CSS and JavaScript URL like
example.css?randomNo = Math.random()
Google Chrome has the Hard Reload as well as the Empty Cache and Hard Reload option. You can click and hold the reload button (in Inspect Mode) to select one.
I recently solved this using Python. Here is the code (it should be easy to adopt to other languages):
def import_tag(pattern, name, **kw):
if name[0] == "/":
name = name[1:]
# Additional HTML attributes
attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
try:
# Get the files modification time
mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
include = "%s?%d" % (name, mtime)
# This is the same as sprintf(pattern, attrs, include) in other
# languages
return pattern % (attrs, include)
except:
# In case of error return the include without the added query
# parameter.
return pattern % (attrs, name)
def script(name, **kw):
return import_tag('<script %s src="/%s"></script>', name, **kw)
def stylesheet(name, **kw):
return import_tag('<link rel="stylesheet" type="text/css" %s href="/%s">', name, **kw)
This code basically appends the files time-stamp as a query parameter to the URL. The call of the following function
script("/main.css")
will result in
<link rel="stylesheet" type="text/css" href="/main.css?1221842734">
The advantage of course is that you do never have to change your HTML content again, touching the CSS file will automatically trigger a cache invalidation. It works very well and the overhead is not noticeable.
You can force a "session-wide caching" if you add the session-id as a spurious parameter of the JavaScript/CSS file:
<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>
If you want a version-wide caching, you could add some code to print the file date or similar. If you're using Java you can use a custom-tag to generate the link in an elegant way.
<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
For ASP.NET I propose the following solution with advanced options (debug/release mode, versions):
Include JavaScript or CSS files this 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 are 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();
}
...
}
If you're using Git and PHP, you can reload the script from the cache each time there is a change in the Git repository, 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;
Simply add this code where you want to do a hard reload (force the browser to reload cached CSS and JavaScript files):
$(window).load(function() {
location.reload(true);
});
Do this inside the .load, so it does not refresh like a loop.
For development: use a browser setting: for example, Chrome network tab has a disable cache option.
For production: append a unique query parameter to the request (for example, q?Date.now()) with a server-side rendering framework or pure JavaScript code.
// Pure JavaScript unique query parameter generation
//
//=== myfile.js
function hello() { console.log('hello') };
//=== end of file
<script type="text/javascript">
document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
// document.write is considered bad practice!
// We can't use hello() yet
</script>')
<script type="text/javascript">
hello();
</script>
For developers with this problem while developing and testing:
Remove caching briefly.
"keep caching consistent with the file" .. it's way too much hassle ..
Generally speaking, I don't mind loading more - even loading again files which did not change - on most projects - is practically irrelevant. While developing an application - we are mostly loading from disk, on localhost:port - so this increase in network traffic issue is not a deal breaking issue.
Most small projects are just playing around - they never end-up in production. So for them you don't need anything more...
As such if you use Chrome DevTools, you can follow this disable-caching approach like in the image below:
And if you have Firefox caching issues:
Do this only in development. You also need a mechanism to force reload for production, since your users will use old cache invalidated modules if you update your application frequently and you don't provide a dedicated cache synchronisation mechanism like the ones described in the answers above.
Yes, this information is already in previous answers, but I still needed to do a Google search to find it.
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 web server's response, in particular the HTTP headers - for how long is this resource valid? Was this resource updated since I last retrieved it? etc.
If things are configured 'correctly', just updating the files of your application should (at some point) refresh the browser's 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 in How Web Caches Work.
Just use server-side code to add the date of the file... that way it will be cached and only reloaded when the file changes.
In ASP.NET:
<link rel="stylesheet" href="~/css/custom.css?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />
<script type="text/javascript" src="~/js/custom.js?d=#(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>
This can be simplified to:
<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>
By adding an extension method to your project to extend Page:
public static class Extension_Methods
{
public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
{
string sFilePath = oPg.Server.MapPath(sRelPath);
string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");
return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
}
}
You can use SRI to break the browser cache. You only have to update your index.html file with the new SRI hash every time. When the browser loads the HTML and finds out the SRI hash on the HTML page didn't match that of the cached version of the resource, it will reload your resource from your servers. It also comes with a good side effect of bypassing cross-origin read blocking.
<script src="https://jessietessie.github.io/google-translate-token-generator/google_translate_token_generator.js" integrity="sha384-muTMBCWlaLhgTXLmflAEQVaaGwxYe1DYIf2fGdRkaAQeb4Usma/kqRWFWErr2BSi" crossorigin="anonymous"></script>

What does the "?" sign mean in a request for a static JS file?

I've seen that a lot and I just don't know what it means. This, for example:
<script src="http://server.com/file.js?y=2345678" type="text/javascript"></script>
If it is in deed possible to 'catch' the value of 'y' in the javascript file, how would that be?
Thank you.
PS. I know what mod_rewrite is and that is not the answer, just in case :)
This is to force the browser not to cache the file, by making it believe that it is a dynamic file with get parameter rather than a static one.
This is often used to facilitate caching of the JS file. You set a far-future Expires header which means the browser may cache it for a very long time. If you change something in the file you also update the number in the querystring, which will make the browser refetch the file. This works because caching is for unique filenames and the querystring is part of the filename (as far as the browser is concerned).
A similar approach to this is to use rewrite rules in the web server to have some part of the file name which it doesnät care about. Here's a Nginx rule to show what I mean:
rewrite ^/style\..*\.css$ /style.css;
I use this rule to have filenames like style.42750cad6.css, which always points to the file style.css. The text in the middle is changed whenever I change style.css. The difference between the first approach is that this does not use the querystring so the caching will work in more browsers.
ok the way i see it in two ways.
it can be used to load js without caching
for every request to the server, the server might log information(if logging is enabled), if i am using it for analytics i can therefore use a different parameter for locations and from the log i can analyse and get required details.

Categories