Programming Thoughts
Struts 2 - Miscellaneous
Page Caching

Cached web pages can show stale data

Application servers present live data and web browsers are smart enough to recognise live data pages but inappropriate caching still happens.

Page Caching

Web browsers and proxies cache web pages and, thus, users can see stale data. Surprisingly, despite being a framework for dynamically generating pages, Struts makes no attempt to disable caching. This is due to the design philosophy of decoupling from the container and environment, leaving such concerns to the application. This is stated in the Migration Guide, Servlet Dependency entry, reproduced below.

Struts 1 Actions have dependencies on the servlet API since the HttpServletRequest and HttpServletResponse is passed to the execute method when an Action is invoked.
Struts 2 Actions are not coupled to a container. Most often the servlet contexts are represented as simple Maps, allowing Actions to be tested in isolation. Struts 2 Actions can still access the original request and response, if required. However, other architectural elements reduce or eliminate the need to access the HttpServetRequest or HttpServletResponse directly.

Tomcat, however, typically defaults to Cache-Control: private (browser caching only) for security controlled pages as most configurations use an authenticator. Look for the disableProxyCaching and securePagesWithPragma attributes.

Fortunately, web browsers are typically smart enough to look at URL pathname extensions and assume .action and .do must be live data. Not always. Web apps have malfunctioned because browsers have suddenly and incorrectly decided voice files from a Struts Action are safe to cache and re-use despite a new record. Developers must decide caching on a case-by-case basis but Struts 2 provides no out-of-the-box help.

Worse, pretty much all web browsers support a back/forward cache (bfcache), where previous pages are cached, retrieved by the back/forward buttons, and is separate from normal caching mechanisms. This can be a problem if a user logs out of a web app and walks away, then another user presses back to see previous pages. This even risks PCI DSS non-compliance. The way to stop this is setting Cache-Control: no-store but there's no built-in setting for this.

Interceptor

Cache control is, of course, set in HttpServletResponse.setHeader() and below is the full code for brute force disabling of caching of all proxies and browsers.

response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setHeader("ETag", null); response.setHeader("Last-Modified", null);

This can be placed in an interceptor for viewer Actions.