javaspringspring-mvcbrowser-cachestatic-resource

Why does Chrome DevTools say my static resource is explicitly non-cacheable while mapped with Spring's mvc:resources?


I'm using Spring MVC 4, and handle static resources as follow (cache for one year):

<mvc:resources mapping="/static/**" location="/static/" cache-period="31556926" />

I have an app.js file in my static folder. When launching an audit with the Chrome DevTools, the app.js file appears in the following section:

The following resources are explicitly non-cacheable. Consider making them cacheable if possible: app.js

The response headers for this static file request are:

Date: Mon, 23 Feb 2015 18:02:49 GMT
Expires: Tue, 23 Feb 2016 23:51:36 GMT
Cache-Control: max-age=31556926, must-revalidate

Are my static resources well cached by browsers? Why is the Chrome DevTools telling me that app.js is explicitly non-cacheable?


Solution

  • I have seen similar oddities with chrome audit and it seems the consensus is that Chrome audit might be buggy:

    Google Chrome audit on caching

    Besides the other answers in that referenced question I have two additional theories that I haven't tested thoroughly but I think might cause the issue:

    1) Cookies

    When you have cookies being passed along with the image which happens quite frequently with Java applications (jsession and various other cookies perhaps set in filters). The problem is that you didn't seem to show us any cookies in your header. Also newer versions of chrome audit typically complain about this as a separate error.

    2) Dynamically requesting content via DOM change

    Some Javascript library particularly ACE edit will dynamically add fonts and or Javascript (aka AMD) and occasionally add query parameters to the URL. This seems to confuse chrome audit. So perhaps your app.js is being added dynamically through some other Javascript (ie adding a <script...src= ></script> element) and/or adding query parameters to the URL?

    UPDATE

    I think I know what your particular issues is. Chrome audit does not like must-revalidate and unfortunately when you specify a cache-period greater than 0 the Spring ResourceHttpRequestHandler will always add the must-revalidate to the cache control header. If you don't set cache-period (it will be -1) then Spring will still do caching headers but will not do must-revalidate (however no max-age leads to another audit error message of The following resources are missing a cache expiration. Resources that do not specify an expiration may not be cached by browsers:).

    Unfortunately you will either need to write a filter to remove the must-revalidate or create your own version of ResourceHttpRequestHandler probably extending and overriding cacheForSeconds.

    Some other options are to use an asset pipeline library like Wro4J or uploading the app.js to your CDN.