ExpiresFilter is a Java Servlet API port of Apache
mod_expires to add 'Expires' and 'Cache-Control: max-age=' headers to HTTP response according to
its 'Content-Type'.
Following documentation is inspired by mod_expires
This filter controls the setting of the Expires HTTP header and the max-age directive of the
Cache-Control HTTP header in server responses. The expiration date can set to be relative to either the time
the source file was last modified, or to the time of the client access.
These HTTP headers are an instruction to the client about the document's validity and persistence. If cached, the document may be fetched from the cache rather than from the source until this time has passed. After that, the cache copy is considered "expired" and invalid, and a new copy must be obtained from the source.
To modify Cache-Control directives other than max-age (see
RFC 2616 section 14.9), you can use
other servlet filters or Apache Httpd
mod_headers module.
Expires' and 'Cache-Control: max-age=' headers to images, CSS and
JavaScript
<web-app ...>
...
<filter>
<filter-name>ExpiresFilter</filter-name>
<filter-class>org.apache.catalina.filters.ExpiresFilter</filter-class>
<init-param>
<param-name>ExpiresByType image</param-name>
<param-value>access plus 10 minutes</param-value>
</init-param>
<init-param>
<param-name>ExpiresByType text/css</param-name>
<param-value>access plus 10 minutes</param-value>
</init-param>
<init-param>
<param-name>ExpiresByType text/javascript</param-name>
<param-value>access plus 10 minutes</param-value>
</init-param>
</filter>
...
<filter-mapping>
<filter-name>ExpiresFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
...
</web-app>
ExpiresByType <content-type>
This directive defines the value of the Expires header and the max-age directive of the
Cache-Control header generated for documents of the specified type (e.g., text/html). The
second argument sets the number of seconds that will be added to a base time to construct the expiration date. The
Cache-Control: max-age is calculated by subtracting the request time from the expiration date and expressing
the result in seconds.
The base time is either the last modification time of the file, or the time of the client's access to the
document. Which should be used is specified by the <code> field; M means that the
file's last modification time should be used as the base time, and
A means the client's access time should be used. The duration
is expressed in seconds. A2592000 stands for
access plus 30 days in alternate syntax.
The difference in effect is subtle. If M (modification in
alternate syntax) is used, all current copies of the document in all caches
will expire at the same time, which can be good for something like a weekly
notice that's always found at the same URL. If A (
access or now in alternate syntax) is used, the date of
expiration is different for each client; this can be good for image files
that don't change very often, particularly for a set of related
documents that all refer to the same images (i.e., the images will be
accessed repeatedly within a relatively short timespan).
Example:
<init-param>
<param-name>ExpiresByType text/html</param-name>
<param-value>access plus 1 month 15 days 2 hours</param-value>
</init-param>
<init-param>
<!-- 2592000 seconds = 30 days -->
<param-name>ExpiresByType image/gif</param-name>
<param-value>A2592000</param-value>
</init-param>
Note that this directive only has effect if ExpiresActive On has
been specified. It overrides, for the specified MIME type only, any
expiration date set by the ExpiresDefault directive.
You can also specify the expiration time calculation using an alternate syntax, described earlier in this document.
ExpiresExcludedResponseStatusCodes
This directive defines the http response status codes for which the
ExpiresFilter will not generate expiration headers. By default, the
304 status code ("Not modified") is skipped. The
value is a comma separated list of http status codes.
This directive is useful to ease usage of ExpiresDefault directive.
Indeed, the behavior of 304 Not modified (which does specify a
Content-Type header) combined with Expires and
Cache-Control:max-age= headers can be unnecessarily tricky to
understand.
Configuration sample :
<init-param>
<param-name>ExpiresExcludedResponseStatusCodes</param-name>
<param-value>302, 500, 503</param-value>
</init-param>
This directive sets the default algorithm for calculating the expiration time
for all documents in the affected realm. It can be overridden on a
type-by-type basis by the ExpiresByType directive. See the
description of that directive for details about the syntax of the argument,
and the "alternate syntax" description as well.
The ExpiresDefault and ExpiresByType directives can also be
defined in a more readable syntax of the form:
<init-param>
<param-name>ExpiresDefault</param-name>
<param-value><base> [plus] (<num> <type>)*</param-value>
</init-param>
<init-param>
<param-name>ExpiresByType type/encoding</param-name>
<param-value><base> [plus] (<num> <type>)*</param-value>
</init-param>
where <base> is one of:
accessnow (equivalent to 'access')modification
The plus keyword is optional. <num> should be an
integer value (acceptable to Integer.parseInt()), and
<type> is one of:
yearsmonthsweeksdayshoursminutessecondsFor example, any of the following directives can be used to make documents expire 1 month after being accessed, by default:
<init-param>
<param-name>ExpiresDefault</param-name>
<param-value>access plus 1 month</param-value>
</init-param>
<init-param>
<param-name>ExpiresDefault</param-name>
<param-value>access plus 4 weeks</param-value>
</init-param>
<init-param>
<param-name>ExpiresDefault</param-name>
<param-value>access plus 30 days</param-value>
</init-param>
The expiry time can be fine-tuned by adding several '
<num> <type>' clauses:
<init-param>
<param-name>ExpiresByType text/html</param-name>
<param-value>access plus 1 month 15 days 2 hours</param-value>
</init-param>
<init-param>
<param-name>ExpiresByType image/gif</param-name>
<param-value>modification plus 5 hours 3 minutes</param-value>
</init-param>
Note that if you use a modification date based setting, the Expires
header will not be added to content that does not come from
a file on disk. This is due to the fact that there is no modification time
for such content.
A response is eligible to be enriched by ExpiresFilter if :
Expires header or the
max-age directive of the Cache-Control header),ExpiresExcludedResponseStatusCodes,Content-Type of the response matches one of the types
defined the in ExpiresByType directives or the
ExpiresDefault directive is defined.Note :
Cache-Control header contains other directives than
max-age, they are concatenated with the max-age directive
that is added by the ExpiresFilter.The expiration configuration if elected according to the following algorithm:
ExpiresByType matching the exact content-type returned by
HttpServletResponse.getContentType() possibly including the charset
(e.g. 'text/xml;charset=UTF-8'),ExpiresByType matching the content-type without the charset if
HttpServletResponse.getContentType() contains a charset (e.g. '
text/xml;charset=UTF-8' -> 'text/xml'),ExpiresByType matching the major type (e.g. substring before
'/') of HttpServletResponse.getContentType()
(e.g. 'text/xml;charset=UTF-8' -> 'text
'),ExpiresDefault
The ExpiresFilter traps the 'on before write response
body' event to decide whether it should generate expiration headers or
not.
To trap the 'before write response body' event, the
ExpiresFilter wraps the http servlet response's writer and
outputStream to intercept calls to the methods write(),
print(), close() and flush(). For empty response
body (e.g. empty files), the write(), print(),
close() and flush() methods are not called; to handle this
case, the ExpiresFilter, at the end of its doFilter()
method, manually triggers the onBeforeWriteResponseBody() method.
The ExpiresFilter supports the same configuration syntax as Apache
Httpd mod_expires.
A challenge has been to choose the name of the <param-name>
associated with ExpiresByType in the <filter>
declaration. Indeed, Several ExpiresByType directives can be
declared when web.xml syntax does not allow to declare several
<init-param> with the same name.
The workaround has been to declare the content type in the
<param-name> rather than in the <param-value>.
The ExpiresFilter has been designed for extension following the
open/close principle.
Key methods to override for extension are :
isEligibleToExpirationHeaderGeneration(HttpServletRequest, XHttpServletResponse)
getExpirationDate(HttpServletRequest, XHttpServletResponse)
To troubleshoot, enable logging on the
org.apache.catalina.filters.ExpiresFilter.
Extract of logging.properties
org.apache.catalina.filters.ExpiresFilter.level = FINE
Sample of initialization log message :
Mar 26, 2010 2:01:41 PM org.apache.catalina.filters.ExpiresFilter init
FINE: Filter initialized with configuration ExpiresFilter[
excludedResponseStatusCode=[304],
default=null,
byType={
image=ExpiresConfiguration[startingPoint=ACCESS_TIME, duration=[10 MINUTE]],
text/css=ExpiresConfiguration[startingPoint=ACCESS_TIME, duration=[10 MINUTE]],
text/javascript=ExpiresConfiguration[startingPoint=ACCESS_TIME, duration=[10 MINUTE]]}]
Sample of per-request log message where ExpiresFilter adds an expiration date
Mar 26, 2010 2:09:47 PM org.apache.catalina.filters.ExpiresFilter onBeforeWriteResponseBody
FINE: Request "/tomcat.gif" with response status "200" content-type "image/gif", set expiration date 3/26/10 2:19 PM
Sample of per-request log message where ExpiresFilter does not add an expiration date
Mar 26, 2010 2:10:27 PM org.apache.catalina.filters.ExpiresFilter onBeforeWriteResponseBody
FINE: Request "/docs/config/manager.html" with response status "200" content-type "text/html", no expiration configured
| Modifier and Type | Class and Description |
|---|---|
| protected static class | |
| protected static enum | ExpiresFilter.
Duration unit |
| protected static class | ExpiresFilter.
Main piece of configuration of the filter. |
| protected static enum | ExpiresFilter.
Expiration configuration starting point. |
| public class | ExpiresFilter.
Wrapping extension of the |
| public class | ExpiresFilter.
Wrapping extension of |
| public class | ExpiresFilter.
Wrapping extension of |
| Modifier and Type | Field and Description |
|---|---|
| private static final Pattern | commaSeparatedValuesPattern
|
| private ExpiresFilter. | defaultExpiresConfiguration
Default Expires configuration. |
| private int[] | excludedResponseStatusCodes
list of response status code for which the |
| private Map | expiresConfigurationByContentType
Expires configuration by content type. |
| private static final String | |
| private static final String | |
| private static final String | |
| private final Log | |
| private static final String | |
| private static final String | |
| private static final String |
| Access | Constructor and Description |
|---|---|
| public |
| Modifier and Type | Method and Description |
|---|---|
| protected static int[] | Returns: nevernull arraycan be commaDelimitedInts)nullConvert a comma delimited list of numbers into an |
| protected static String[] | Returns: array of patterns (nonnull)the string to be split commaDelimitedStrings)Convert a given comma delimited list of strings into an array of String |
| protected static boolean | |
| public void | doFilter(ServletRequest
The request to process request, ServletResponse The response associated with the request response, FilterChain Provides access to the next filter in the chain for this filter to pass the request and response
to for further processing chain)Implements jakarta. The |
| public ExpiresFilter. | |
| public String | |
| public int[] | |
| protected Date | Returns: the expiration dateThe HTTP request request, ExpiresFilter.The wrapped HTTP response responseReturns the expiration date of the given |
| protected Date | Returns: the expiration dateThe parsed expires configuration,The Servlet response response
Returns the expiration date of the given |
| public Map | |
| protected Log | |
| public void | init(FilterConfig
The configuration information associated with the filter instance being initialised filterConfig)Overrides org. Overrides default jakarta. Iterates over the configuration parameters and either logs a warning, or throws an exception for any parameter that does not have a matching setter in this filter. |
| protected static String | Returns: a comma separated stringThe int array ints)Convert an array of ints into a comma delimited string |
| protected boolean | Returns: true if an expire header may be addedThe Servlet request request, ExpiresFilter.The Servlet response response
|
| protected static boolean | |
| protected static boolean | Returns: true if the given str has at least one character (can be a whitespace).The String to check str)
|
| public void | onBeforeWriteResponseBody(HttpServletRequest
The Servlet request request, ExpiresFilter.The Servlet response response
If no expiration header has been set by the servlet and an expiration has been defined in the
|
| protected ExpiresFilter. | Returns: the parsed expiresthe input inputLine)Parse configuration lines like ' |
| public void | |
| public void | |
| public void | setExpiresConfigurationByContentType(Map<String, ExpiresFilter.
|
| protected static boolean | Returns: true if the given string starts with the given prefix ignoring case.can be string, String nullcan be prefix)null
|
| protected static String | Returns: the subset of the givenstr that is before the first occurrence of the given separator.
Return null if the given str or the given separator is null. Return and empty
string if the separator is empty.can be str, String nullcan be separator)null
|
| public String |