Preventing Cacheable HTTP Response
Browsers may store a local cached copy of content received from web servers. Cached content could be retrieved by other users of the same computer later, and this could be a problem if the content contains sensitive information. A web server – or a specific
web application on a server - can be configured to give directives to browsers to not store local cache copies of response content.
In Tomcat, you can control client-side caching by creating a filter of the type ExpiresFilter, which is a Java Servlet API port of Apache Module mod_expires. This filter controls the setting of the following properties in server responses:
- Expires HTTP header
- Cache-Control: max-age HTTP header
The expiration date can be set relative to either the time the source file was last modified, or to the time of the client access. The Cache-Control header turns on client-side caching and sets the max-age of a resource before it is expired. The Expires header is used to specify a specific point in time the resource is no longer valid. In practice, when both the Expires header and the Cache-Control: max-age header are set, the max-age will take precedence.
An ExpiresFilter in Tomcat is created in the configuration file web.xml to do the following:
- Create a filter that applies to all web applications running on the same Tomcat server, you configure the filter in the file $CATALINA_BASE/conf/web.xml.
- Create a filter that applies to Panopticon only, you configure the filter in the file $CATALINA_BASE/webapps/panopticon/WEB-INF/web.xml.
The Apache Tomcat 9 documentation on https://tomcat.apache.org/tomcat-9.0-doc/config/filter.html#Expires_Filter
has some examples of how such a filter could be constructed. In addition to the filter definition, there must also be a filter-mapping. The Tomcat documentation has examples of this as well.
In addition to the Apache Tomcat documentation examples, here is another one that shows how a filter can be constructed, which makes any content not specified explicitly to expire immediately:
<filter>
<filter-name>ExpiresFilter</filter-name>
<filter-class>org.apache.catalina.filters.ExpiresFilter</filter-class>
<init-param>
<!-- specific content type expiry rules go here -->
</init-param>
<!-- Let everything else expire immediately -->
<init-param>
<param-name>ExpiresDefault</param-name>
<param-value>access plus 0 seconds</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ExpiresFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
NOTE: The filter-mapping comes after the filter definition.
Additional Cache-Control Directives
There are additional instructions about caching that can be given from the server to the client, through directives of the Cache-Control header. Here are some examples:
- Cache-Control: public means resources can be cached by any intermediate proxies along the way between server and end-client.
- Cache-Control: private means resources can only be cached by the end-client.
- Cache-Control: no-cache means that the resource may indeed be cached, but it is an instruction to the client that it must revalidate with the server every time before using a cached version of the resource.
- Cache-Control: no-store means that the client is now allowed to cache any resource. The resource must be requested, and a full response downloaded, from the server each time. This is a directive commonly used with sensitive data.
Legacy HTTP Header Pragma
Pragma is the HTTP/1.0 implementation and cache-control is the HTTP/1.1 implementation (since 1999) of the same concept. They both are meant to prevent the client from caching the response. Older clients may not support HTTP/1.1 which is why that header
is still in use. Pragma is a legacy of HTTP/1.0 and hasn't been needed since Internet Explorer 5, or Netscape 4.7.
Creating a Custom filter for Cache-control with Tomcat
To use Cache-Control directives in the Cache-Control header with Tomcat, you must write a custom filter. Below is an example of such a filter. It also sets the Expires header to a time in the past, assuring that any cached content is immediately expired. It also sets the legacy HTTP 1.0 Pragma header.
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
public class CacheControlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws java.io.IOException, ServletException
{
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Expires", "Tue, 03 Jul 2001 06:00:00 GMT");
resp.setDateHeader("Last-Modified", new java.util.Date().getTime());
resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0,
post-check=0, pre-check=0");
resp.setHeader("Pragma", "no-cache");
chain.doFilter(request, response);
}
}
Copy the filter code to a file named CacheControlFilter.java, compile and package using the commands below:
avac -cp /tomcat/lib/servlet-api.jar CacheControlFilter.java
jar cf CacheControlFilter.jar CacheControlFilter.class
Place the .jar file in $CATALINA_BASE/lib/
The filter is enabled by adding the below in <tomcat>/conf/web.xml:
<filter>
<filter-name>SetCacheControl</filter-name>
<filter-class>CacheControlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SetCacheControl</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(c) 2013-2024 Altair Engineering Inc. All Rights Reserved.