Symphonious

Living in a state of accord.

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.

  • Odi says:

    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 19, 2007 at 7:30 pm
  • danny says:

    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 19, 2007 at 7:39 pm
  • Adrian Sutton says:

    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 19, 2007 at 7:42 pm
  • Adrian Sutton says:

    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 19, 2007 at 8:06 pm
  • danny says:

    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 20, 2007 at 12:14 am
  • danny says:

    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 :-)

    June 20, 2007 at 2:07 am
  • Arun says:

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

    March 5, 2008 at 4:05 pm
  • Bilal says:

    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 23, 2008 at 7:18 am
  • Arun says:

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

    September 23, 2008 at 2:46 pm
  • Gopal says:

    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 7, 2009 at 6:17 pm
  • Gopal says:

    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 7, 2009 at 9:22 pm
  • Gopal says:

    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 7, 2009 at 9:51 pm
  • Arun says:

    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 7, 2009 at 9:55 pm
  • Gopal says:

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

    March 7, 2009 at 11:46 pm
  • Pawan says:

    Thanks,
    My problem get solved, (not opening flash file using SSL)

    February 24, 2010 at 9:15 am
  • Karthi says:

    Guys,

    I am also having the same kind of problem, am using tomcat server for my web application, in that i just want to cache static objects like (images, javascripts, & CSS). how can i do that…

    Can anyone give me a working solution….

    Your help much appreciated…

    March 9, 2010 at 5:27 am
  • Karthi says:

    @danny: Thanks. The issue is resolved.

    March 9, 2010 at 11:33 am
  • Karthik says:

    Hi danny..

    Thank you very much for posting the web.xml and filter. It solved our problem too..

    Thanks,
    Karthik

    October 14, 2010 at 11:00 am
  • hakimeh says:

    Hi
    I am haveing same problem.tomcat cash static file like (.xml and ..)
    can anyone me a working solution?

    thanks
    hakimeh

    November 1, 2010 at 11:20 am

Your email address will not be published. Required fields are marked *

*