Bug 65513

Summary: Change in browser cache behavior introduced in 9.0.51
Product: Tomcat 9 Reporter: S.T. Nagel <scott.t.nagel>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Severity: normal CC: michaelo
Priority: P2    
Version: 9.0.52   
Target Milestone: -----   
Hardware: PC   
OS: Linux   
Attachments: Debugging panel from IE 11

Description S.T. Nagel 2021-08-20 12:31:57 UTC
My web application uses a transport-guarantee of CONFIDENTIAL in the web.xml security-constraint.  When using Tomcat 9.0.43, the response headers include "Expires: Jan 1, 1970" and user browsers will not cache any of the pages from my application.  After upgrading to Tomcat 9.0.52, the "Expires" header is no longer present and user browsers are suddenly caching pages.  This unexpected change in behavior is leading to breakage in my web application.

The change in behavior appears to be related to this changelog message:
> Fix: To avoid unnecessary cache revalidation, do not add an HTTP Expires header when setting adding an HTTP header of CacheControl: private. (markt) 
The commit message associated with the change seems to be focused on the behavior of proxy caches and it's not clear that the behavior of browser caches was taken into account.

Admittedly, my web application should be more robust in regards to explicitly setting Cache-Control and Expires headers on pages where caching cannot be tolerated.  However, mine is unlikely to be the only web application that is inadvertently and unknowingly relying on the pre-9.0.51 behavior that automagically disabled browsers from caching web application pages when security constraints are present.

I think this breaking change should be reverted from Tomcat 9.x. The behavior has existed for the entire lifecycle of Tomcat 9 and the change does not appear to have been driven by a bug report (at least not one that I could find or one that was referenced in the commit), so the adage "if it ain't broke, don't fix it" may be applicable. The optimization is a good improvement, but is probably more appropriate for Tomcat 10 where the the change in behavior is acceptable as part of a major version upgrade.  If the change remains in Tomcat 9, it should be documented in the migration guide since users may need to update configuration and/or code to account for the change in behavior.
Comment 1 Mark Thomas 2021-08-20 17:16:29 UTC

CONFIDENTIAL only implies private caching, not no caching.

Enabling securePagesWithPragma="true" for your authenticator may be a viable work-around until the web application is fixed.

I'm currently leaning towards closing this as WONTFIX.
Comment 2 Christopher Schultz 2021-08-20 17:51:18 UTC
So, we can't revert the change because the reasoning behind it was legitimate and it's an appropriate change.

On the other hand, you are right, Scott, the behavior did change in a potentially unexpected way.

Note that Tomcat is not Debian, where absolutely nothing whatsoever is expected to change between versions other than fixing security problems. Behavior-causing changes are not forbidden within point-releases, here.

The changelog does contain an entry announcing the change. The changelog is the appropriate place to go when looking for changes between releases.

There is a "Notable Changes" section on the web site which in this case did not get an entry added. I think it would be appropriate to add an item to this list:


The only way to effectively "undo" this change would be to install a RewriteValve into the default configuration which replicated pre-9.0.51 behavior. That seems a little heavy-handed and does not help anyone who uses their own (e.g. revision-controlled) configuration.

Finally, the "Expires" header is only part of the equation. I haven't tested this, but if you want to prevent Google Chrome from storing your pages in the bfcache (which isn't the disk!), you will need to use "Cache-Control: no-store" and the "Expires" header has no effect.

I tend to agree that no further code/configuration changes should be made, but that the documentation can certainly be improved.
Comment 3 S.T. Nagel 2021-08-20 19:18:02 UTC
I agree that this is a legitimate and appropriate change, but I wanted to express my concerns about the impacts of making the change in Tomcat 9. You've acknowledged my concerns and provided background and explanation for the change, so I'm quite satisfied. I'll have no heartache with closing WONTFIX.

I do strongly believe that this change warrants an entry on the "Notable Changes" section of the web site (what I referred to as the migration guide based on the URL).  For me personally, I read the changelog as items of interest, but I read the notable changes as things I may need to update in my configuration/code when upgrading.

I understand that "CONFIDENTIAL only implies private caching, not no caching", but Tomcat 9 has always implemented it as no caching.  However, I'm moving on - I understand that the issue has been fixed and the behavior has changed.

I appreciate the suggested workaround idea of setting securePagesWithPragma="true".  I will look into it, but I do have a strict requirement for "Cache-Control: private", so I'm not sure the securePagesWithPragma branch will satisfy my needs.

Thank you gentlemen for your informative and thorough responses.
Comment 4 Christopher Schultz 2021-08-20 19:56:46 UTC
(In reply to S.T. Nagel from comment #3)
> I appreciate the suggested workaround idea of setting
> securePagesWithPragma="true".  I will look into it, but I do have a strict
> requirement for "Cache-Control: private", so I'm not sure the
> securePagesWithPragma branch will satisfy my needs.

I mentioned the RewriteValve above but I think it cannot set headers for you the way that tuckey's url-rewrite can. Consider asking on the users' mailing-list for help with forcing an Expires header on your responses.
Comment 5 Michael Osipov 2021-09-01 09:11:32 UTC
We have been hit by this on 8.5.70 as well. The application runs with IE 11 and it polls for a status resource. After the first request it serves from browser cache and never updates. Therefore, the poll repeats forever. See attached screenshot. The application is old. I remember similar issues with JSPs which have been cached by IE as well years ago. Our "solution" was to add ?rnd={unixtime}.
Comment 6 Michael Osipov 2021-09-01 09:11:54 UTC
Created attachment 38007 [details]
Debugging panel from IE 11
Comment 7 Michael Osipov 2021-09-01 09:41:33 UTC
So what I do in affected servlet is:
response.setHeader("Cache-Control", "no-cache");

This really needs to go to notable changes.
Comment 8 Christopher Schultz 2021-11-17 20:06:44 UTC
I finally added this to the "Notable Changes" sections of the various migration guides in r1895119.

Sorry this took so long to get to.