What does appending "?v=1" to CSS and JavaScript URLs in link and script tags do?
Asked Answered
K

8

150

I have been looking at a HTML 5 boilerplate template (from http://html5boilerplate.com/) and noticed the use of "?v=1" in URLs when referring to CSS and JavaScript files.

  1. What does appending "?v=1" to CSS and JavaScript URLs in link and script tags do?
  2. Not all JavaScript URLs have the "?v=1" (example from the sample below: js/modernizr-1.5.min.js). Is there a reason why this is the case?

Sample from their index.html:

<!-- CSS : implied media="all" -->
<link rel="stylesheet" href="css/style.css?v=1">

<!-- For the less-enabled mobile browsers like Opera Mini -->
<link rel="stylesheet" media="handheld" href="css/handheld.css?v=1">

<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
<script src="js/modernizr-1.5.min.js"></script>

<!------ Some lines removed ------>

<script src="js/plugins.js?v=1"></script>
<script src="js/script.js?v=1"></script>

<!--[if lt IE 7 ]>
  <script src="js/dd_belatedpng.js?v=1"></script>
<![endif]-->


<!-- yui profiler and profileviewer - remove for production -->
<script src="js/profiling/yahoo-profiling.min.js?v=1"></script>
<script src="js/profiling/config.js?v=1"></script>
<!-- end profiling code -->
Kunkel answered 12/8, 2010 at 11:11 Comment(0)
S
193

These are usually to make sure that the browser gets a new version when the site gets updated with a new version, e.g. as part of our build process we'd have something like this:

/Resources/Combined.css?v=x.x.x.buildnumber

Since this changes with every new code push, the client's forced to grab a new version, just because of the querystring. Look at this page (at the time of this answer) for example:

<link ... href="http://sstatic.net/stackoverflow/all.css?v=c298c7f8233d">

I think instead of a revision number the SO team went with a file hash, which is an even better approach, even with a new release, the browsers only forced to grab a new version when the file actually changes.

Both of these approaches allow you to set the cache header to something ridiculously long, say 20 years...yet when it changes, you don't have to worry about that cache header, the browser sees a different querystring and treats it as a different, new file.

Stopover answered 12/8, 2010 at 11:14 Comment(27)
Note that, of course, this hash (should it be based on the file’s contents) should not be re-calculated on each page request… ;)Amplifier
@Scytale - I would say that need not be said, but unfortunately I've seen a commercial document management system that did that before, on 5+ gig worth of files every time every client did a sync to compare the contents :(Stopover
-1 for knowing about cache control headers, yet advertising these URL horrors.Razorback
@Free - A cache control header sent yesterday can't tell the client the file changed today (the client won't even check), a URL can. Can you explain what I'm missing there?Stopover
User? Server should not talk to user instead of his browser. You are missing a HTTP protocol and its fine grain control of telling UA when to ask for modified entity. Effectively allowing border proxy to perform a DoS on your web app, too :-)Razorback
@Free - The way these files are cached is forever, meaning the client is in no way checking to see if the file is modified. This means they would never get the updated file...unless the URL changed, which is what happens with the technique above. You get maximum cache life on the client (fewest HTTP requests) but the client is instantly updated when the file actually changes. Exactly how would you accomplish all this using only cache control headers?Stopover
When one ask a compliant UA to store entity forever, and when figure out what it has been expired and feel sudden desire for UA to ask if it has been modified, whose fault it is? Here we are, a kludge to support a previous kludge to support previous kludge. With reasonable entity age it works properly in the natural way.Razorback
Hashing the file modification date is good enough for all practical purposes, and is (in my experience) an inexpensive operation even if done in real time.Parisi
@Free - Stack Overflow gets 5 million visitors a month, your approach would have 2 impacts: a) many more requests and data sent to/from our servers, and b) the users wouldn't immediately get new JavaScript/CSS (for example when we had a bug, or the HTML changes needing new JS/CSS). Natural expiration really isn't an option here. The method you're proposing would be technically much less efficient and result is actual user bugs, on a regular basis...that isn't really acceptable on any major site (nor should it be om any site really).Stopover
a.1) not many more if configured properly a.2) requests ending in 304 do not produce substantial load and b) in HTTP nothing happens immediately, when you break something in the production environment caching users might not even notice, which works as traffic shaper. Anyway, 1.9 visitors per second × 7 requests per page (requisites) isnt really horrifying number, given how much work done client-side (yes, i'm talking about what given instead of custom tag feeds)Razorback
@Free - The 5 million is 5 million visitors per month, since we deploy many times a day, the requests are many times that. In terms of HTML page-loads we're talking a little over 110 million per month (and growing...again that's only HTML page loads). For a) yes, many more, or more breaks, it's a trade-off either way on the cache time vs clients having correct content. Also, your logic for b) is flawed, the html is not cached, so used with cached JS that no longer works means only cached users are affected, not that they're immune.Stopover
Yes, i have a calculator, estimated number requests which should be served by border cache and was not really horrified. b) implies what caches must be coherent, otherwise is it quite pointless. Time for me to cease trying to convince the convinced.Razorback
@Nick, the code snippet in the original question was taken from HTML5BP; which has removed the query-string versioning in favor of filename revving due to some issues with proxy servers and caching (learn more from Steve Souders). There is a little more work involved with filename revving since some URL rewriting needs to be set up.Patagonia
Also, not a big deal, but might be worth mentioning: by using hashes instead of file name versions you basically are giving the middle finger to your GZIP compression algo (you should send RAW DEFLATE instead, btw) as it can't do crap to compress strings like ef2f2383c884 and c7993dfe1bc5. Granted, the hashing technique is easier to implement that versioning.Patagonia
I remember that I used the hash way before, and the browser didn't get my last updated file, and as you may see in this screen shot screencast.com/t/tgNRhHMB I can't see where stackoverflow is using this hash, and Also I can't understand why hash way could be better then with the query string.Dufrene
@David Murdoch: Regarding your last comment - what are you talking about? c298c7f8233d (the hash) is not inside the entity body (the thing that gets compressed).Arlettearley
@thirtydot, I think you may have overlooked the fact that the browser will have to download the hash when it downloads the HTML for the page. :-pPatagonia
@David Murdoch: Yeah, I totally overlooked that :) Time for critique Plan B: what difference does 12 bytes make in a multi kilobyte HTML page? Then again you did already say it's "not a big deal". I'm just going to shut up.Arlettearley
@David - Versions are much more complicated to implement with very little benefit and much more overhead to the entire build process. A hash is very simple, and therefore very easy to maintain (and with a CDN, makes life much simpler as well). We're talking a few bytes here for where the alternative is (comparatively) a world of hurt, which would you choose? Currently our hashing has no dependency on the build or what version of the code is deployed, it only needs the file contents to hash, which are readily available, you can see the obvious choice for us here.Stopover
@Nick I agree. And admit that I'm a bit of a hypocrite since I don't use versions in my current MVC boilerplate setup (though I hash on LastModified instead of file contents)Patagonia
Hi, is "v" parameter fixed? is there any other parameters that I can find?Susquehanna
@GMsoF v just represents "version" for us, it's a completely arbitrary choice. Any value query string would work, e.g. it could just as easily be ?jejdutogjesudo=Stopover
@NickCraver Will adding version number allow this file to be cached? test.js?v=1 will request new file every time or only for initial time and then cache and use it unless url is changed?Phlogopite
@NickCraver where/how do you cache the file hashes? It seems kinda of time expensive.Padron
@Phlogopite the hash needs to change or cache needs to expire...the point is you know when the contents change so change the querystring when that happens.Stopover
@Padron we use a simple dictionary that calculates if missing to get the hashes - so we're reading and hashing the file just once per app lifetime...it's really cheap. Since a build for almost all websites starts and stops that specific app domain or process, a simple in-memory cache works very well. When the files change, you're re-calcing anyway.Stopover
9 years later, is this still applicable? Is there any official documentation on this feature?Hilbert
D
29

This makes sure you are getting the latest version from of the css or js file from the server.

And later you can append "?v=2" if you have a newer version and "?v=3", "?v=4" and so on.

Note that you can use any querystring, 'v' is not a must for example:

"?blah=1" will work as well.

And

"?xyz=1002" will work.

And this is a common technique because browsers are now caching js and css files better and longer.

Dufrene answered 12/8, 2010 at 11:12 Comment(0)
M
13

The hash solution is nice but not really human readable when you want to know what version of file is sitting in your local web folder. The solution is to date/time stamp your version so you can easily compare it against your server file.

For example, if your .js or .css file is dated 2011-02-08 15:55:30 (last modification) then the version should equal to .js?v=20110208155530

Should be easy to read properties of any file in any language. In ASP.Net it's really easy...

".js?v=" + File.GetLastWriteTime(HttpContext.Current.Request.PhysicalApplicationPath + filename).ToString("yyMMddHHHmmss");

Of coz get it nicely refactored into properties/functions first and off you go. No more excuses.

Good luck, Art.

Malti answered 8/2, 2011 at 16:10 Comment(2)
What if you are building your website only with html js and css . Then how can we automatically inject version name to static resources?Plater
@Whizkid747 late reply, but for newcomers, whatever site builder / build system you're using should have a way to get the date in milliseconds which you can use as the version, otherwise if you're not using a builder//build system you'd have to write these yourself.Longeron
A
10

In order to answer you questions;

"?v=1" this is written only beacuse to download a fresh copy of the css and js files instead of using from the cache of the browser.

If you mention this query string parameter at the end of your stylesheet or the js file then it forces the browser to download a new file, Due to which the recent changes in the .css and .js files are made effetive in your browser.

If you dont use this versioning then you may need to clear the cache of refresh the page in order to view the recent changes in those files.

Here is an article that explains this thing How and Why to make versioning of CSS and JS files

Alcibiades answered 8/10, 2013 at 12:50 Comment(0)
B
7

Javascript files are often cached by the browser for a lot longer than you might expect.

This can often result in unexpected behaviour when you release a new version of your JS file.

Therefore, it is common practice to add a QueryString parameter to the URL for the javascript file. That way, the browser caches the Javascript file with v=1. When you release a new version of your javascript file you change the url's to v=2 and the browser will be forced to download a new copy.

Bronwen answered 12/8, 2010 at 11:14 Comment(1)
which browsers exactly? even most quirky IE 5, and 6 were obeying cache control headers.Razorback
C
2

During development / testing of new releases, the cache can be a problem because the browser, the server and even sometimes the 3G telco (if you do mobile deployment) will cache the static content (e.g. JS, CSS, HTML, img). You can overcome this by appending version number, random number or timestamp to the URL e.g: JSP: <script src="js/excel.js?time=<%=new java.util.Date()%>"></script>

In case you're running pure HTML (instead of server pages JSP, ASP, PHP) the server won't help you. In browser, links are loaded before the JS runs, therefore you have to remove the links and load them with JS.

// front end cache bust
var cacheBust = ['js/StrUtil.js', 'js/protos.common.js', 'js/conf.js', 'bootstrap_ECP/js/init.js'];   
for (i=0; i < cacheBust.length; i++){
     var el = document.createElement('script');
     el.src = cacheBust[i]+"?v=" + Math.random();
     document.getElementsByTagName('head')[0].appendChild(el);
}
Craal answered 2/6, 2016 at 6:9 Comment(0)
U
0

As you can read before, the ?v=1 ensures that your browser gets the version 1 of the file. When you have a new version, you just have to append a different version number and the browser will forget about the old version and loads the new one.

There is a gulp plugin which takes care of version your files during the build phase, so you don't have to do it manually. It's handy and you can easily integrate it in you build process. Here's the link: gulp-annotate

Unconditioned answered 14/4, 2017 at 17:10 Comment(0)
C
-2

As mentioned by others, this is used for front end cache busting. To implement this, I have personally find grunt-cache-bust npm package useful.

Contraceptive answered 24/1, 2018 at 18:11 Comment(1)
While this link may answer the question, link only answers are discouraged on Stack Overflow, you can improve this answer by taking vital parts of the link and putting it into your answer, this makes sure your answer is still an answer if the link gets changed or removed :)Cypriot

© 2022 - 2024 — McMap. All rights reserved.