Caching in Tomcat - SOLVED!
It took forever, but I've finally how to stop Tomcat adding a Pragma: no-cache header to any resources in a secure context. You need to set disableProxyCaching to false, so if you're using basic authentication you need a valve like:
<Valve className="org.apache.catalina.authenticator.BasicAuthenticator" disableProxyCaching="false" />
That needs to go within the <context> definition for your web-app. So, within the META-INF (some sources say within WEB-INF?) create a context.xml file like:
<Context><Valve className="org.apache.catalina.authenticator.BasicAuthenticator" disableProxyCaching="false" /></Context>
I found deploying as a WAR didn't work (it seemed to ignore the context.xml), but deploying the exploded files did work and that's fine by me.
Combine that with the awesome cache control filter that Danny posted in the comments and you have a very nice, easily configurable caching mechanism that doesn't cause browsers to download the CSS and JavaScript resources for every single page.
Dear lazyweb,
Does anyone have best practices for implementing correct, useful caching for a webapp running in Tomcat? I'm using container managed security and it's happily adding Pragma: No-cache to everything, including the static CSS file which is pretty brain dead. It looks like I can add a custom filter to get rid of that header and then manage caching myself, but it seems like something that everyone should have solved before so I thought I'd ask….
Thanks,
Lazy me.

June 19th, 2007 at 7:30 pm
Adrian,
Web caching is a difficult task. Many who have tried to implement it, have failed. (I have personally experienced the completely broken implementation of the Magnolia CMS). The best cache I know is Squid. For caches the cache headers are extremely important. Unfortunately most content management software completely ignores these headers and provides no possibility for the content managers to control them.
So the first thing is to get control of the cache headers. Without them, the cache can not know what to do.
The second thing is to use Squid as a frontend to Tomcat. Do not be tempted to write your own cache. Do not be tempted to cache inside your servlet container. Maybe even set up an Apache HTTPD with mod_jk between Squid and Tomcat. Apache may give you more control over cache headers (you can rewrite them based on the URL if necessary). Apache can also help in devlivering static content like the CSS, that doesn’t need to go through your Webapp.
Cheers
Odi
June 19th, 2007 at 7:39 pm
Adrian,
I’m not sure how Tomcat adds the no cache to everything, you’ll need to work that out yourself, perhaps it only doesn it if you haven’t done anything?
The easy way to control the headers yourself is to write and configure a generic header filter in web.xml as follows:
=== web.xml ===
NoCache
x.y.z.filters.ResponseHeaderFilter
Cache-Control
no-cache, must-revalidate
CacheForWeek
uk.co.slc.crm.common.filters.ResponseHeaderFilter
Cache-Control
max-age=604800, public
NoCache
/*.do
CacheForWeek
/images/*
CacheForWeek
/*.js
CacheForWeek
/*.css
=== web.xml ===
=== x.y.z.filters.ResponseHeaderFilter ===
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResp = (HttpServletResponse)resp;
Enumeration e = config.getInitParameterNames();
while (e.hasMoreElements()) {
String headerName = (String)e.nextElement();
String headerValue = config.getInitParameter(headerName);
httpResp.addHeader(headerName, headerValue);
}
chain.doFilter(req, resp);
}
=== x.y.z.filters.ResponseHeaderFilter ===
June 19th, 2007 at 7:42 pm
Thanks Odi,
I agree web caching is messed up regularly. In my particular case I’m trying to avoid causing headaches for people who need to set up caches in front of my web app and take advantage of browser caches as much as possible since it will be accessed from both sides of the pacific so anything we can do to speed it up is a good thing.
My main focus at this stage (until actual performance data shows I need to do something more) is just to get some sane headers in place at the URL level (ie: not just Pragma: no-cache for everything). I want to avoid requiring squid or apache to do that just for ease of deployment which is why I was hoping someone had experience particularly with configuring Tomcat.
Also, the idea of caching here isn’t to reduce workload on the server, just to reduce the amount of data that needs to be transferred to the client. So fortunately there’s no need to try and set up caching in the servlet etc, just need to get the headers right so browsers and any available proxies can cache content as needed.
June 19th, 2007 at 8:06 pm
Danny,
That looks pretty much exactly like the strategy I was just starting down. From what I’ve read, Tomcat adds the Pragma no-cache whenever the resources are in a secure context which isn’t a bad default. You can set them back to null though so that should solve the problem.
June 20th, 2007 at 12:14 am
Oops the xml has been emasculated by your blogging s’ware.
Try this:
<filter>
<filter-name>NoCache</filter-name>
<filter-class>x.y.z.filters.ResponseHeaderFilter</filter-class>
<init-param>
<param-name>Cache-Control</param-name>
<param-value>no-cache, must-revalidate</param-value>
</init-param>
</filter>
<filter>
<filter-name>CacheForWeek</filter-name>
<filter-class>x.y.z.filters.ResponseHeaderFilter</filter-class>
<init-param>
<param-name>Cache-Control</param-name>
<param-value>max-age=604800, public</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>NoCache</filter-name>
<url-pattern>/*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheForWeek</filter-name>
<url-pattern>/images/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheForWeek</filter-name>
<url-pattern>/*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheForWeek</filter-name>
<url-pattern>/*.css</url-pattern>
</filter-mapping>
June 20th, 2007 at 2:07 am
also.. the variety of special headers that control caching is large in http 1.1 and non existent in http 1.0. This [1] is a must-read. Remember that while all browsers probably support http 1.1 the same may not be assumed for proxies and caches. MSIE 6 has a setting which defaults it to use http 1.0 through proxies.
[1]http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
focus on sections 14.9, 14.21, 14.25, 14.28, 14.29 & 14.32 :-)
July 10th, 2007 at 1:05 pm
[...] and hence the application cannot open the file. After much searching around the internet, I found this post which describes how to do [...]
July 30th, 2007 at 11:16 pm
[...] článek o tom, jak vypnout automatické nastavování “no-cache” v Tomcatu [...]
February 19th, 2008 at 8:55 pm
[...] finer control, please have a look at Symphonious’ article on that [...]
March 5th, 2008 at 4:05 pm
I’m having a similar problem…. I’m working on a spring based appl. This xml doesnt seem to affect the way tomcat sets cahce-control for a spring web appl.. may be i’m doing something worng, can anyone pls help..
September 23rd, 2008 at 7:18 am
Arun, I’m also working on a Spring based app and want to set the cache control for it. Were you able to solve this? I’d be very grateful for any tips.
September 23rd, 2008 at 2:46 pm
I wasn’t able to solve it using the XML method said here.
I was able to set the filter in the web.xml and wrote a servlet to set the header for every response..
I found out that http://www.xucia.com/#Resource%20Accelerate Resource Accelerate was very helpful, we just need to add the jar into the app, and make some simple changes in the web.xml.. It also caches the content in the web server also. It worked out gr8 for me..