By implementing a cache, and making full use of the caching mechanisms and Cache-Control directives that are available to your application, you can improve speed, energy usage, and user experience.
When a file is cached, it is available immediately for reuse, which makes an application appear faster. Enabling a cache, and making full use of Cache-Control directives, reduces the amount of data and data connections that are sent needlessly. These savings can help keep a user beneath their data cap, keep their battery from draining as quickly, and improve the responsiveness of wireless networks that have limited capacity.
Despite these benefits, some applications don't use Cache-Control directives at all, and many don't use caching mechanisms to their fullest extent.
The HTTP 1.1 protocol supports cache management through the Cache-Control general-header of each request and response message. Once cache control functionality is enabled, the server places directives in the Cache-Control field that can be used by the client to determine if a resource should be pulled from the client-side cache, or if it must be requested from the server.
This Best Practice Deep Dive looks at the caching mechanisms and Cache-Control directives that are specified in the HTTP/1.1 Protocol. It examines the issue of why caching is important, and provides recommendations for how to implement a cache and use caching mechanisms and Cache-Control directives in an application.
Caching significantly improves performance by eliminating as many requests to the server, and full server responses, as possible. The HTTP/1.1 Protocol specifies two main caching mechanisms for achieving this: Validation and Expiration.
Validation is the process by which a cache entry is checked against the origin server (or against an intermediate cache) to validate that it is still usable. When a server generates a full response, it attaches a validator to it, which the client keeps with the cache entry for that resource. When the client makes a request for a resource, for which it has a cache entry, it includes the validator in the request. The server checks the validator in the request against the current validator for the resource.
If the validators match, the server sends a 304 (Not Modified) response, and the client uses the cached entry. This mechanism saves a full server response, because the information does not need to be downloaded again. If the validators do not match, the server sends the full information, but an extra round trip is eliminated by validating the cached entry up front.
Expiration is the caching mechanism by which a client can entirely avoid making requests to the origin server. When the origin server specifies an explicit expiration time in the resource, a cache can check that expiration time and respond accordingly without having to contact the server first. The server specifies the expiration time of an entity by using an Expires header, or by including the max-age Cache-Control directive in a response. It's important to note that the max-age directive takes priority over an Expires header. When the max-age directive is present in a response entity that is cached, the response entity is considered to be stale if its current age is greater than the max-age value.
These two cache mechanisms (validation and expiration) are implicit directives to a cache as to how resources should be updated. To fully manage a cache, however, a server or client may need to provide explicit directives. This is what the Cache-Control header is used for. The explicit directives in the Cache-Control header, take precedence over the implicit directives in both caching mechanisms. An example of this is the max-age directive which takes priority over any expiration time that is set in an Expires header.
Cache-Control directives follow the syntax:
Cache-Control: cache-request-directive, cache-response-directive
The following is a list of the directives that can be used in the Cache-Control general-header field to specify behavior that must be obeyed by all caching mechanisms in the request/response chain.
max-age "=" delta-seconds
max-stale "=" delta-seconds
min-fresh "=" delta-seconds
private [ "=" <"> 1 # field-name <"> ]
no-cache [ "=" <"> 1 # field-name <"> ]
max-age "=" delta-seconds
s-maxage "=" delta-seconds
token [ "=" (token | quoted-string) ]
Cache-Control directives fall into different general categories. Some can only be imposed by the origin server, others can only be imposed by the user agent, and some can be imposed by either. The following table shows these categories.
|Type of Directive||Can be Imposed by||Directives|
|Restrictions on which responses to consider cacheable.||Origin server||public, private, no-cache|
|Restrictions on what may be stored by a cache.||Origin server or user agent||no-store|
|Modifications of the basic expiration mechanism.||Origin server or user agent||s-maxage, max-age, min-fresh, max-stale|
|Controls over cache revalidation and reload.||User agent||max-age, only-if-cached, must-revalidate, proxy-revalidate|
|Control over transformation of entities.||User agent||no-transform|
|Extensions to the caching system.||User agent||cache-extension|
The following example shows a typical use of the Cache-Control general header field. The public directive indicates that the Response Entity is cacheable, and the max-age directive specifies its expiration time. In this example, the Response Entity has a shelf life of one week. Based on the Last-Modified timestamp, it should be consider stale after Fri. 04 Mar 2011, 07:06 GMT.
HTTP/1.1 200 OK
Last-Modified: Fri, 25 Feb 2011 15:01:28 GMT
Cache-Control: public, max-age=576342
Date: Fri, 25 Feb 2011 23:05:05 GMT
Note: For more detailed information about the Cache-Control general-header and the cache control directives listed in this section, see the HTTP/1.1 Protocol Specification (RFC2616), Section 13 (titled Caching in HTTP), hosted by the World Wide Web Consortium (W3C).
Some applications do not implement a cache, and many do not make full use of the caching mechanisms and Cache-Control directives in HTTP/1.1. This leads to unnecessary downloads that waste energy and make an application appear slower.
While it's true that downloading one 4KB image unnecessarily, may not waste much energy. That amount can grow exponentially as your application gains exposure and that image is downloaded thousands of times.
If your application requests a 4 KB image twice per session with just 5,000 users, you're sending 19 MB of "extra" data to your users. The radio power usage on these 5,000 additional downloads is equivalent to draining 35% of a sample smart phone battery.
Best Practice Recommendation
The Best Practice Recommendation is to implement a cache in your application, and to make full use of the caching mechanisms and Cache-Control directives in HTTP/1.1.
A cache is a process that runs locally, as a service. It behaves transparently, and operates as a middle-man standing in between the client and server processes. A cache serves locally-stored copies of Response Entities, and can be referred to as a Response Entity Cache.
There are several ways to implement caching. There are libraries available for this, and some operating systems have functions for it, but the most direct approach is to incorporate Response Entity Cache functionality into your client software code. This is done by implementing a class that wraps a collection of Response Entities. This class should encapsulate a searchable container of Response Entity objects and include methods for:
Transparently intercepting Inbound Response Messages.
Determining if a Response Entity is Cacheable.
Adding Cacheable Response Entities to the collection.
Transparently intercepting Outbound Request Messages.
- Checking to see if the associated Response Entity is a cached item.
- If not (a cache miss), relaying the Request Message to the Origin Server.
- If yes (a cache hit), determine if it is appropriate to serve the cached response.
- If it is, then serve the cached response.
Note: For information on creating cache storage in Android, see Using External Storage in the Data Storage section of the Android Developer's Guide.
Whenever a client requires a resource that has been downloaded previously, it should use the version that is in the cache — not the one on the origin server. If, and only if, the cache is unable to fulfill the request, should the cache pass the Request Message on to the origin server on the client's behalf.
Re-transmitted resources generally have the correct caching information, so even with a cache implemented in your application, it's important to use this information and make use of the Expiration and Validation caching mechanisms.
By checking the expiration time of the resource, or by using the max-age Cache-Control directive, an application can make use of the Expiration mechanism and often eliminate the overhead of a Request/Response cycle.
By storing validators with cached resources and including them in server requests, an application can make use of the Validation mechanism and often eliminate the overhead of a full response from the server.
Using the explicit Cache-Control directives, such as max-age, allows you to have even more control over the expiration and validation of resources in your cache. To understand what each of the directives do, see the HTTP/1.1 Protocol Specification (RFC2616), Section 13 (titled Caching in HTTP), hosted by the World Wide Web Consortium (W3C).
For more specific recommendations about managing cache expiration, see Cache Expiration.