Leverage browser caching
Setting an expiry date or a maximum age in the HTTP headers for static resources instructs the browser to load previously downloaded resources from local disk rather than over the network.
HTTP/S supports local caching of static resources by the browser. Some of the newest browsers (e.g. IE 7, Chrome) use a heuristic to decide how long to cache all resources that don't have explicit caching headers. Other older browsers may require that caching headers be set before they will fetch a resource from the cache; and some may never cache any resources sent over SSL.
To take advantage of the full benefits of caching consistently across all browsers, we recommend that you configure your web server to explicitly set caching headers and apply them to all cacheable static resources, not just a small subset (such as images). Cacheable resources include JS and CSS files, image files, and other binary object files (media files, PDFs, Flash files, etc.). In general, HTML is not static, and shouldn't be considered cacheable.
HTTP/1.1 provides the following caching response headers :
Cache-Control: max-age. These specify the “freshness lifetime” of a resource, that is, the time period during which the browser can use the cached resource without checking to see if a new version is available from the web server. They are "strong caching headers" that apply unconditionally; that is, once they're set and the resource is downloaded, the browser will not issue any GET requests for the resource until the expiry date or maximum age is reached.
ETag. These specify some characteristic about the resource that the browser checks to determine if the files are the same. In the
Last-Modifiedheader, this is always a date. In the
ETagheader, this can be any value that uniquely identifies a resource (file versions or content hashes are typical).
Last-Modifiedis a "weak" caching header in that the browser applies a heuristic to determine whether to fetch the item from cache or not. (The heuristics are different among different browsers.) However, these headers allow the browser to efficiently update its cached resources by issuing conditional GET requests when the user explicitly reloads the page. Conditional GETs don't return the full response unless the resource has changed at the server, and thus have lower latency than full GETs.
is important to specify one of
and one of
for all cacheable resources. It is
redundant to specify both
or to specify both
- Set caching headers aggressively for all static resources.
- For all cacheable resources, we recommend the following
Expiresto a minimum of one month, and preferably up to one year, in the future. (We prefer
Cache-Control: max-agebecause it is is more widely supported.) Do not set it to more than one year in the future, as that violates the RFC guidelines.
If you know exactly when a resource is going to change, setting a shorter expiration is okay. But if you think it "might change soon" but don't know when, you should set a long expiration and use URL fingerprinting (described below). Setting caching aggressively does not "pollute" browser caches: as far as we know, all browsers clear their caches according to a Least Recently Used algorithm; we are not aware of any browsers that wait until resources expire before purging them.
- Set the
Last-Modifieddate to the last time the resource was changed. If the
Last-Modifieddate is sufficiently far enough in the past, chances are the browser won't refetch it.
- Use fingerprinting to dynamically enable caching.
- For resources that change occasionally, you can have the browser cache the resource until it changes on the server, at which point the server tells the browser that a new version is available. You accomplish this by embedding a fingerprint of the resource in its URL (i.e. the file path). When the resource changes, so does its fingerprint, and in turn, so does its URL. As soon as the URL changes, the browser is forced to re-fetch the resource. Fingerprinting allows you to set expiry dates long into the future even for resources that change more frequently than that. Of course, this technique requires that all of the pages that reference the resource know about the fingerprinted URL, which may or may not be feasible, depending on how your pages are coded.
- Set the Vary header correctly for Internet Explorer.
- Internet Explorer does not cache any resources that are
served with the
Varyheader and any fields but
User-Agent. To ensure these resources are cached by IE, make sure to strip out any other fields from the Vary header, or remove the Vary header altogether if possible
- Avoid URLs that cause cache collisions in Firefox.
- The Firefox disk cache hash functions can generate collisions for URLs that differ only slightly, namely only on 8-character boundaries. When resources hash to the same key, only one of the resources is persisted to disk cache; the remaining resources with the same key have to be re-fetched across browser restarts. Thus, if you are using fingerprinting or are otherwise programmatically generating file URLs, to maximize cache hit rate, avoid the Firefox hash collision issue by ensuring that your application generates URLs that differ on more than 8-character boundaries.
- Use the
Cache control: publicdirective to enable HTTPS caching for Firefox.
- Some versions of Firefox require that the
Cache control: publicheader to be set in order for resources sent over SSL to be cached on disk, even if the other caching headers are explicitly set. Although this header is normally used to enable caching by proxy servers (as described below), proxies cannot cache any content sent over HTTPS, so it is always safe to set this header for HTTPS resources.
For the stylesheet used to display the user's calendar after login, Google Calendar embeds a fingerprint in its filename: calendar/static/fingerprint_keydoozercompiled.css, where the fingerprint key is a 128-bit hexadecimal number. At the time of the screen shot below (taken from Page Speed's Show Resources panel), the fingerprint was set to 82b6bc440914c01297b99b4bca641a5d:
The fingerprinting mechanism
allows the server to set the
one year ahead of the request date; the
header to the date the file was last modified; and the
max-age header to 3153600. To cause the client to
re-download the file in
case it changes before its expiry date or maximum age, the fingerprint
(and therefore the URL) changes whenever the file's content does.
- For an in-depth explanation of HTTP caching, see the HTTP/1.1 RFC, sections 13.2, 14.21, and 14.9.3.
- For details on enabling caching in Apache, consult the Apache Caching Guide.
Leverage proxy caching
Enabling public caching in the HTTP headers for static resources allows the browser to download resources from a nearby proxy server rather than from a remote origin server.
In addition to browser caching, HTTP provides for proxy caching, which enables static resources to be cached on public web proxy servers, most notably those used by ISPs. This means that even first-time users to your site can benefit from caching: once a static resource has been requested by one user through the proxy, that resource is available for all other users whose requests go through that same proxy. Since those locations are likely to be in closer network proximity to your users than your servers, proxy caching can result in a significant reduction in network latency. Also, if enabled proxy caching effectively gives you free web site hosting, since responses served from proxy caches don't draw on your servers' bandwidth at all.
You use the
indicate that a resource can be cached by public web
proxies in addition to the browser that issued the
some exceptions (described below), you should configure your web server
set this header to
public for cacheable
- Don't include a query string in the URL for static resources.
- Most proxies, most notably Squid up through version 3.0,
do not cache resources with a "?" in their URL even if a
Cache-control: publicheader is present in the response. To enable proxy caching for these resources, remove query strings from references to static resources, and instead encode the parameters into the file names themselves.
- Don't enable proxy caching for resources that set cookies.
- Setting the header to public effectively shares resources
multiple users, which means that any cookies set for those resources
shared as well. While many proxies won't actually cache any resources
with cookie headers set, it's better to avoid the risk altogether.
Either set the
privateor serve these resources from a cookieless domain.
- Be aware of issues with proxy caching of JS and CSS files.
- Some public proxies have bugs that do not detect the
Content-Encodingresponse header. This can result in compressed versions being delivered to client browsers that cannot properly decompress the files. Since these files should always be gzipped by your server, to ensure that the client can correctly read the files, do either of the following:
- Set the the
private. This disables proxy caching altogether for these resources. If your application is multi-homed around the globe and relies less on proxy caches for user locality, this might be an appropriate setting.
- Set the
Vary: Accept-Encodingresponse header. This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. The correct version of the resource is delivered based on the client request header. This is a good choice for applications that are singly homed and depend on public proxies for user locality.
- Set the the