Caching issues when serving static files with servlet container

I've been working with computers for many years and it still surprises me that obvious five minute job can take you several hours to complete.

The initial situation was simple. When I changed a static file on my webserver (Tomcat 6), this change wasn't reflected in a browser. I thought that with all my knowledge of web applications this should have been an easy one for me, but it turned out that it takes some time to sort things out.

Default servlet

My first suspicion was that Tomcat's default servlet is not checking the modification time of the resource. Fortunately, this wasn't the case and I found it out by going through it's code. (Doing that I found out the strange fact that If-None-Match HTTP header takes precedence over Cache-Control and I suspected this is the problem for a while...)

cachingAllowed attribute

Next, there's cachingAllowed attribute in the container context settings described as

If the value of this flag is true, the cache for static resources will be used. If not specified, the default value of the flag is true.

My second suspicion was that Tomcat is caching it's static resources (and therefore sending back 304 Not Modified status), but it wasn't the case again. Tomcat caching is either checking the file modification time (and reloads the cache) or the cache time is too short to be the cause. I don't know exactly because I didn't dig deeper - several tests with http headers dumper was enough for me and led me to the final solution.

HTTP caching directives

The final one to blame is Cache-Control HTTP header. Tomcat's default servlet didn't send this header so that caching algorithms could be used along the request/response chain, which means even in the browser itself.

This results in a situation where browser is not even asking server for some of the resources. There's emphasis on those words, because for example the caching algorithm behavior in Firefox is unpredictable according to my HTTP headers dump (Which confused me so much that I didn't think about this possibility earlier). I think this caching algorithm is somehow based on number of changes to the resource in the past, but again, I don't care.

Solution

The abstract solution is simple. Server must send Cache-Control: no-cache HTTP header with static resources that is changed often. The bad thing is that I haven't found any settings concerning this in any configuration documentation. It has to be done manually with filters.

Although it should be a simple and straightforward filter, it's better to use something already done. I found such a simple headers filter. It's transparent and open-sourced so that you can check it manually and prepare your own build (don't you ever dare to put binaries on your server from an unknown source). And now my web.xml settings looks like this:


  httpHeaders
  
    org.ft.servlet.filters.httpheaders.HTTPHeadersFilter
  
  
    Cache-Control
    no-cache
  
  
    Pragma
    no-cache
  
  
    negativeMapping
    .*\.xsl|.*\.js|.*\.css
  



  httpHeaders
  /v2/*

]]>

Doesn't look like a five minute job any more, heh?

Last update
201002270000