Using HTTP caching
Traffic Server can cache HTTP and HTTPS responses from an application to improve page load speed. On a site which serves a large amount of infrequently changing pages to anonymous users, caching can provide a significant improvement to both performance and page load times. It's not uncommon to see caching improve a site's capacity from tens of requests per second to tens of thousands of requests per second, without using any additional resources (except for bandwidth, of course).
Configuring caching
Caching is enabled on Ingress resources by default. To indicate that a response
is to be cached, your application should send a Cache-Control
header field in
the response:
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Content-Type: text/html; charset=UTF-8
...
Like most HTTP header fields, this is a comma-separated list of values. public
indicates that the page content does not contain private data (meaning it should
be cached by multi-user proxies like Traffic Server), and max-age
indicates how
long it should be cached for; in this case, 3600 seconds or one hour.
As an alternative to Cache-Control
, your application can send an Expires
header field containing a timestamp; the page will be cached until the expiry
time is reached. Expires
is not recommended for new applications, since
Cache-Control
is more flexible and has better defined semantics.
Disabling caching
If you want to prevent certain pages from being cached, you can indicate this
using Cache-Control
:
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
You should generally do this for any personalised pages, e.g. pages served to
logged-in users, or pages containing a Set-Cookie
header field. By default,
TS will not use the cache for requests containing a Cookie
header field or
cache responses containing a Set-Cookie
field, but it's better to be explicit.
To disable caching entirely on an Ingress, even if it sends Cache-Control
or
Expires
header fields, use the ingress.kubernetes.io/cache-enable
annotation:
metadata:
annotations:
ingress.kubernetes.io/cache-enable: "false"
If only some paths should have caching disabled, you can create another Ingress resource for that particular path:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/cache-enable: "false"
name: echoheaders
spec:
rules:
- host: www.mysite.path
http:
paths:
- path: /admin
backend:
serviceName: myapp
servicePort: http
Controlling downstream caching
When a page is cached in Traffic Server, an application can easily remove the page from the cache when it changes. This allows the application to set a long cache lifetime and rely on cache purging to keep content up to date.
However, the application cannot purge content from clients or downstream caches,
such as ISP or organisational proxies. This means if your Cache-Control
header indicates the response should be cached for 30 days, the client may never
fetch fresh content until 30 days have passed, even if the document is updated
before then in the TS cache.
In some cases, like static assets which have cache-aware URLs, the content for a particular URL will never change, and this behaviour is desirable. However, it's generally not desirable for HTML, where the document content often changes without the URL changing.
To prevent clients or downstream proxies from caching responses, you can set the
X-Next-Hop-Cache-Control
header field in the response. If present, TS will
use this value to replace the Cache-Control
header field, but still use the
original Cache-Control
for its own caching.
A suitable value for HTML content using a 30-day cache lifetime would be:
X-Next-Hop-Cache-Control: no-cache, max-age=2592000
This means that clients can still cache the document for up to 30 days, but the
no-cache
directive requires the document to be revalidated with the origin
server before it's used. Since the document should be in the TS cache,
validation should be quick and won't require the document to be re-sent unless
it's changed.
Caching and URL parameters
When a page is cached, its URL parameters are stored in the cache to ensure that a request with different URL parameters returns the correct content. For example, the URL:
http://www.mysite.com/listings/?page=1
will be cached differently from the URL:
http://www.mysite.com/listings/?page=2
Usually this is what you want and no additional configuration is required.
However, sometimes clients may request pages with additional URL parameters
which do not affect page content. A good example of this is marketing tracking
parameters like utm_medium
which are used by JavaScript on the page to
identify traffic sources, but do not affect the page content at all. Because
these URL parameters do not affect page content, they should not be considered
when caching. (The JavaScript tracking code will run anyway, so no data is
lost.)
There are two approaches to configuring this: either you can set a list of URL parameters which should be ignored when caching (which is the safest method), or you can set a whitelist of parameters, where any parameters not in the list will be ignored.
To exclude a set of parameters from caching, set the
ingress.kubernetes.io/cache-ignore-query-params
annotation on the Ingress:
ingress.kubernetes.io/cache-ignore-query-params: "utm_* source_id"
The value should be a list of UNIX globs (*
, ?
and [...]
are supported);
any matching query parameters will be ignored for caching.
To set a whitelist of URL parameters, set the
ingress.kubernetes.io/cache-whitelist-query-params
annotation:
ingress.kubernetes.io/cache-whitelist-query-params: "page view include_id_*"
The format is the same as cache-ignore-query-params
, but the meaning is
reversed: any URL parameter not matched will be ignored.
When using either of these annotations, you probably also want to set
ingress.kubernetes.io/cache-sort-query-params: "true"
, which will cause the
URL parameters to be lexically sorted. This means that a request for
/?a=1&b=2
can be served a cached response for/?b=2&a=1
, improving cache hit
rate across clients.
These annotations also change the query string sent to the application. This is to ensure the application doesn't accidentally vary the page content based on a query parameter that has been ignored for caching.
Caching and cookies
Usually, TS will not cache a response or serve a request from the cache if either of the following are true:
- The request has a
Cookie
header field - The response has a
Set-Cookie
header field
This is true even if the response includes an explicit Cache-Control
, and is
intended to avoid accidental leakage of private data due to misconfiguration.
However, in some cases your users may have cookies set that do not affect page
content. This is common with tracking cookies for page analytics tools; Google
Analytics, for example, sets several cookies with names like __utma
, which are
only ever referenced from JavaScript and do not affect page content.
To allow caching to work even with these cookies set, use the
ingress.kubernetes.io/cache-ignore-cookies
annotation:
metadata:
annotations:
ingress.kubernetes.io/cache-ignore-cookies: "utm_* has_js"
The annotation value should be a space-delimited list of UNIX globs; any cookie
in the request Cookie:
header field which matches one of the globs will be
removed. If there are no cookies left, then the entire Cookie
header field
will be removed from the request; the request can then be served from the cache,
or its response can be stored in the cache.
You can also set a whitelist of cookies; any cookie whose name is not in this list will be removed from the request:
metadata:
annotations:
ingress.kubernets.io/cache-whitelist-cookies: "SESS* csrftoken"
If any cookies remain after processing, caching will be bypassed as normal.
If both cache-ignore-cookies
and cache-whitelist-cookies
are configured,
a cookie will only be permitted if it both matches the whitelist and does not
match the ignore list.
Removing pages from the cache
Removing individual URLs
You can purge individual URLs from the cache by sending an HTTP PURGE
request
to Traffic Server. To make this easy to do from pods, create a Service for the
TS pod. The PURGE request should look like this:
PURGE http://www.mysite.com/mypage.html HTTP/1.0
Unfortunately, this doesn't work very well when multiple copies of TS are running, since there's no simple way for an application to retrieve the list of TS instances. We plan to address this in a future release.
Clearing the entire cache
Occasionally, you might want to clear the cache for an entire domain, for
example if some boilerplate HTML has changed that affects all pages. To do this,
set the ingress.kubernetes.io/cache-generation
annotation to a non-zero
integer. This changes the cache generation for the Ingress; any objects cached
with a different generation are no longer visible, and have been effectively
removed from the cache. Typically the cache generation would be set to the
current UNIX timestamp, although any non-zero integer will work.
Caching and static assets
Static assets can be purged from the cache when they change in the same way as HTML content. However, this is quite inefficient: whenever the application is redeployed and static assets have changed, either the entire cache needs to be purged, or the individual URLs of each asset need to be purged. Even then, this will not purge content from downstream caches, meaning some users may not see your updates. While you could prevent clients from caching static assets, this has a deleterious effect on performance.
Instead, your application should change the URL of the assets when their content changes. There are three common ways to do this:
- Define a global "style version" in the application, and append this to the
URL as a query parameter:
/css/main.css?42
; - Check the timestamp of each asset, and append it to the URL, again as a
query parameter:
/css/main.css?1496405714
; - Calculate the hash of the content of each asset, and include this as either
a part of the filename or a query parameter:
/css/main.abcd1234.css
or/css/main.css?abcd1234
.
Because the URL of the asset now changes when its content changes, clients will automatically fetch the correct version of the asset, without having to clear anything from the cache (except the HTML, of course). In addition, the assets can be cached by clients or downstream proxies without the risk that out-of-date cached content will be used.
This is an extremely common pattern for caching static assets, so support is available for doing it automatically in most web application frameworks.
X-Cache-Status
header
When an Ingress has caching enable, TS will include an X-Cache-Status
header
in the HTTP response, which looks like this:
X-Cache-Status: hit-fresh (1496232521)
The number in brackets is the current cache generation configured on the Ingress. The first word is the cache lookup status, which can be:
miss
: the document was not found in the cache, and TS went to the origin to fetch it.hit-fresh
: a valid copy of the document was found in the cache, and TS served this copy without going to the origin.hit-stale
: a stale copy of the document was found in the cache (e.g., it had exceeded itsmax-age
), and TS went to the origin to revalidate the document. The document may still have been served from the cache if the origin returned HTTP 304 (Not modified).skipped
: TS did not do a cache lookup for the document. This status is most common with internally-generated responses, such as errors and redirects.