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..
March 7th, 2009 at 6:17 pm
hi Arun or anyone,
do you have the complete sample that you can share, (web.xml, servlets etc) that worked for you.
we need to shrink he download size, increase the speed, reduce the # of requests from client/browser to server, ensure data is found on the hard disk than the memory of the client, etc etc. so that the web application that we are working on first checks the local systems hard disk for cached resources (js, imgs/gifs/pngs, css, text, properties etc) before it even makes a requests.. to the server.
all we want to do is ensure that all subsequent requests get their data from local hard disk cache till the data on server has changed.
regards
gopal.
March 7th, 2009 at 9:22 pm
My request is what do I need to do (or if an example is present) to make sure that the response is built and then given to the Resource accelerator filter classes so that the gzipinng and caching headers are modified/updated before the response is sent to the client/browser.I think the Spring MVC dispatcher servlet gets the request and it is bypassing the filter classes to do the gzipinng and caching of HTTP headers and sending the response to clients/browsers.
Please find my web.xml – I have added the Resource Accelerator jar to the build path and mentioned the filter class and mappings in web.xml.
MES
CacheFilter
com.xucia.resourceaccelerate.CacheHeadersFilter
<!–
cache-folder ra-cache
–>
cache-folder
WEB-INF/cache
expiration-time
86400000
CompressAndCacheFilter
com.xucia.resourceaccelerate.CompressAndCacheHeadersFilter
caching-headers-enabled
true
expiration-time
86400000
cached-compressed-data
true
JSCompressAndCacheFilter
com.xucia.resourceaccelerate.CompressAndCacheHeadersFilter
expiration-time
86400000
shrink-js
false
CacheFilter
*.gif
CacheFilter
*.jpg
CacheFilter
*.png
CacheFilter
*.jpeg
CompressAndCacheFilter
*.html
CompressAndCacheFilter
*.css
JSCompressAndCacheFilter
*.js
dispatcher
org.springframework.web.servlet.DispatcherServlet
contextClass
org.springframework.web.context.support.XmlWebApplicationContext
contextConfigLocation
/WEB-INF/server-prod-context.xml /WEB-INF/presentation-prod-context.xml /WEB-INF/presentation-planning-context.xml /WEB-INF/server-planning-context.xml /WEB-INF/server-quality-context.xml /WEB-INF/presentation-quality-context.xml
1
SendForSchdDDServlet
in.jsw.mes.production.servlet.SendForSchdDDServlet
OrdersServlet
in.jsw.mes.planning.transactions.servlets.OrdersServlet
ReductionMasterServlet
in.jsw.mes.planning.masters.servlets.ReductionMasterServlet
StartupPossibleValuesServlet
in.jsw.mes.planning.masters.servlets.StartupPossibleValuesServlet
HrCoilRcptServlet
in.jsw.mes.production.servlet.HrCoilRcptServlet
CPLProductionServlet
in.jsw.mes.production.servlet.CPLProductionServlet
QualityServlet
in.jsw.mes.quality.servlet.QualityServlet
DelayServlet
in.jsw.mes.production.servlet.DelayServlet
DefectServlet
in.jsw.mes.quality.servlet.DefectServlet
surfaceImageDisplay
surfaceImageDisplay
in.jsw.mes.quality.servlet.surfaceImageDisplay
ReductionMasterServlet
/ReductionMasterServlet
StartupPossibleValuesServlet
/StartupPossibleValuesServlet
dispatcher
*.*
SendForSchdDDServlet
/SendForSchdDDServlet
OrdersServlet
/OrdersServlet
HrCoilRcptServlet
/HrCoilRcptServlet
CPLProductionServlet
/CPLProductionServlet
QualityServlet
/QualityServlet
DelayServlet
/DelayServlet
DefectServlet
/DefectServlet
surfaceImageDisplay
/surfaceImageDisplay
index.jsp
http://java.sun.com/jsp/jstl/core
/WEB-INF/tld/jstl-core.tld
http://java.sun.com/jsp/jstl/fmt
/WEB-INF/tld/jstl-fmt.tld
http://java.sun.com/jsp/jstl/fn
/WEB-INF/tld/jstl-fn.tld
/spring
/WEB-INF/tld/spring.tld
Example Security Constraint
Secured Area
*.ui
<!–
friend
–>
FORM
Secured Application Area
/WEB-INF/jsps/production/transaction/login.jsp
/WEB-INF/jsps/production/transaction/login.jsp?error=Login+not+successful.+Try
again.
<!–
friend
–>
March 7th, 2009 at 9:51 pm
Regret that I am flooding with questions – however I am happy to seek the right fraternities help
1. Or – may be – all requests are to be served by Dispather servlet of Spring – but when the response is composed – it should be given to the Resource Accelerator Servlet so that the headers are appropriately set/updated – before the response is sent to the clients/browsers.
2. The objective that is to be achieved is that -
a. Set longer expiry period of the css, js, imgs/png, imgs/gif etc at the client side.
b. So that requests are not sent to the server for such mime types.
c. However once all the headers are set appropriately – the subsequent request for such mime types should only be if-modified-since – and as you know if there are no modifications nothing is sent and if modifications are there -only the modified resouces should be sent back to be again cached for the defined expiry period
d. instead of storing the cache in the memory can the cache be stored on the hard disk -
Regards,
gopal.
March 7th, 2009 at 9:55 pm
Hi Gopal,
I could not get the whole tags from the response you posted..
In web.xml you need to use <filter> tags so that Tomcat will know to send all requests thru this Resource Accelerate.
March 7th, 2009 at 11:46 pm
MES
CacheFilter
com.xucia.resourceaccelerate.CacheHeadersFilter
cache-folder
WEB-INF/cache
expiration-time
86400000
CompressAndCacheFilter
com.xucia.resourceaccelerate.CompressAndCacheHeadersFilter
caching-headers-enabled
true
expiration-time
86400000
cached-compressed-data
true
JSCompressAndCacheFilter
com.xucia.resourceaccelerate.CompressAndCacheHeadersFilter
expiration-time
86400000
shrink-js
false
CacheFilter
*.gif
CacheFilter
*.jpg
CacheFilter
*.png
CacheFilter
*.jpeg
CompressAndCacheFilter
*.html
CompressAndCacheFilter
*.css
JSCompressAndCacheFilter
*.js
dispatcher
org.springframework.web.servlet.DispatcherServlet
contextClass
org.springframework.web.context.support.XmlWebApplicationContext
contextConfigLocation
/WEB-INF/server-prod-context.xml /WEB-INF/presentation-prod-context.xml /WEB-INF/presentation-planning-context.xml /WEB-INF/server-planning-context.xml /WEB-INF/server-quality-context.xml /WEB-INF/presentation-quality-context.xml
1
SendForSchdDDServlet
in.jsw.mes.production.servlet.SendForSchdDDServlet
OrdersServlet
in.jsw.mes.planning.transactions.servlets.OrdersServlet
ReductionMasterServlet
in.jsw.mes.planning.masters.servlets.ReductionMasterServlet
StartupPossibleValuesServlet
in.jsw.mes.planning.masters.servlets.StartupPossibleValuesServlet
HrCoilRcptServlet
in.jsw.mes.production.servlet.HrCoilRcptServlet
CPLProductionServlet
in.jsw.mes.production.servlet.CPLProductionServlet
QualityServlet
in.jsw.mes.quality.servlet.QualityServlet
DelayServlet
in.jsw.mes.production.servlet.DelayServlet
DefectServlet
in.jsw.mes.quality.servlet.DefectServlet
surfaceImageDisplay
surfaceImageDisplay
in.jsw.mes.quality.servlet.surfaceImageDisplay
ReductionMasterServlet
/ReductionMasterServlet
StartupPossibleValuesServlet
/StartupPossibleValuesServlet
dispatcher
*.ui
SendForSchdDDServlet
/SendForSchdDDServlet
OrdersServlet
/OrdersServlet
HrCoilRcptServlet
/HrCoilRcptServlet
CPLProductionServlet
/CPLProductionServlet
QualityServlet
/QualityServlet
DelayServlet
/DelayServlet
DefectServlet
/DefectServlet
surfaceImageDisplay
/surfaceImageDisplay
index.jsp
http://java.sun.com/jsp/jstl/core
/WEB-INF/tld/jstl-core.tld
http://java.sun.com/jsp/jstl/fmt
/WEB-INF/tld/jstl-fmt.tld
http://java.sun.com/jsp/jstl/fn
/WEB-INF/tld/jstl-fn.tld
/spring
/WEB-INF/tld/spring.tld
Example Security Constraint
Secured Area
*.ui
<!–
friend
–>
FORM
Secured Application Area
/WEB-INF/jsps/production/transaction/login.jsp
/WEB-INF/jsps/production/transaction/login.jsp?error=Login+not+successful.+Try again.
<!–
friend
–>