12

I am using code like this:

bundles.Add(new ScriptBundle("~/bundles/textview")
                    .Include(
                        "~/Scripts/printarea/jquery.PrintArea.js",
                        "~/Scripts/pagedown/Markdown.Converter.js",
                        "~/Scripts/pagedown/Markdown.Sanitizer.js",
                        "~/Scripts/pagedown/Markdown.Editor.js"
                    ));

This creates a file with a cache expiry date of one year in advance and this is what appears in my script HTML when I look at the source:

<script src="/bundles/textview?v=cNvP0r6Jo6hsl2Sdzhw-o3kAK7t2JdcNWiG0iIg7Lys1"></script>

So why do I in fiddler still see it going to the server to check if the file has been modified ? Is there a way that the bundle routine could be modified so that it does not add the ?v= and instead simply appends the GUID to the filename with for example a hyphen in between?

  • What exactly does fiddler get as an answer? – TGlatzer Jan 13 '14 at 08:14
  • And another question: what does the Network-Pane of the developer Tools (F12-Tools) of the Browsers state? – TGlatzer Jan 13 '14 at 08:15
  • Can this extension help you https://github.com/unger/Bundling.Extensions ? – Irvin Dominin Jan 13 '14 at 08:18
  • This extension looks good but I'm concerned about how well this works with the later versions of MVC. The documentation is minimal and there's nothing to say what it's compatible with. –  Jan 13 '14 at 13:12
  • Check this question, it appears that only chrome has this problem, http://stackoverflow.com/questions/18557251/why-does-browser-still-sends-request-for-cache-control-public-with-max-age – Akash Kava Jan 19 '14 at 06:37

3 Answers3

6

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

Why a server check ?

Browsers use a freshness heuristic to determine if they should validate a resource with the server or just pull it from the cache.

The browser will serve cached files without validating them with the server unless one of the following is true:

  1. The freshness heuristic is not met (that is, the file in the cache is not considered fresh).
  2. You have changed the expires header or other caching header.
  3. You have set the browser to disable caching..
  4. The URL to the resource changes or is different.

Adding a Web.config file to the Scripts folder:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <staticContent>
             <clientCache cacheControlMode="UseExpires" 
                          httpExpires="Thu, 01 Jan 2016 00:00:00 GMT" />
        </staticContent>
    </system.webServer>
</configuration>

This sets the Expires Header out for a year. this will allow your files to be served directly from the cache without checking with the server for the next year.

As for bundles, headers are explicitly set inside System.Web.Optimization.dll:

private static void SetHeaders(BundleResponse bundle, BundleContext context)
{
    if (context.HttpContext.Response != null)
    {
        if (bundle.ContentType != null)
        {
            context.HttpContext.Response.ContentType = bundle.ContentType;
        }
        if (!context.EnableInstrumentation && context.HttpContext.Response.Cache != null)
        {
            HttpCachePolicyBase cache = context.HttpContext.Response.Cache;
            cache.SetCacheability(bundle.Cacheability);
            cache.SetOmitVaryStar(true);
            cache.SetExpires(DateTime.Now.AddYears(1));
            cache.SetValidUntilExpires(true);
            cache.SetLastModified(DateTime.Now);
            cache.VaryByHeaders["User-Agent"] = true;
        }
    }
}

so you need to check that you are not breaking any of the rules forcing the browser to check with the server!

references:


Update

If your target is to have your scripts always emitted as:

<script src="/Scripts/printarea/jquery.PrintArea.js"></script>
<script src="/Scripts/pagedown/Markdown.Converter.js"></script>
<script src="/Scripts/pagedown/Markdown.Sanitizer.js"></script>
<script src="/Scripts/pagedown/Markdown.Editor.js></script>

Rather than:

<script src="/bundles/textview?v=cNvP0r6Jo6hsl2Sdzhw-o3kAK7t2JdcNWiG0iIg7Lys1"></script>

Then add the following to the RegisterBundles method (disabling Bundling and Minification):

BundleTable.EnableOptimizations = false;

Unless EnableOptimizations is true or the debug attribute in the compilation Element in the Web.config file is set to false, files will not be bundled or minified. Additionally, the .min version of files will not be used, the full debug versions will be selected. EnableOptimizations overrides the debug attribute in the compilation Element in the Web.config file

MK.
  • 5,139
  • 1
  • 22
  • 36
1

Probably has to do with the cache-busting querystring variable that MVC appends to the URL:

Don't include a query string in the URL for static resources.

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

ref: https://developers.google.com/speed/docs/best-practices/caching

Mister Epic
  • 16,295
  • 13
  • 76
  • 147
  • Agreed but the whole point of the GUID at the end is to make it unique which makes me wonder why they don't replace the "?v=" with a hyphen. That way you get a unique name and caching without the need to do a check. –  Jan 10 '14 at 14:54
1

When you say "still see it going to the server to check if the file has been modified" what's the return code? IE will always make a request for items it has cached, such as bundles. It includes the ETAG & LAST-MODIFIED headers in the request. IIS usually replies with 304 not modified & no content. Firefox on the other hand wont make any request at all. It's just how the browser handles caching.

The v querystring is just to make sure we're asking for the right file. Since IE will always make the request & get a 304 back, IE isn't really bothered by the v string. However Firefox with its aggressive caching will see the slightly different url & make a request for the file. If the parameter wasn't there, Firefox wouldn't no to request a new file when the bundle changed.

I'm not sure what Chrome does, but suspect it behaves similarly to Firefox.

Simon Halsey
  • 5,459
  • 1
  • 21
  • 32