HTTP Cache Headers

HTTP caching can speed up web applications. It’s standardized and well implemented in all modern browsers and results in a lower latency app and improved responsiveness.

HTTP caching occurs when an item is fully cached for faster retrieval the next time the resource is required. The browser may choose to not contact the server at all and simply use its own local copies of web resources. There are many cache headers such as “Cache-Control”, “Expires”, “Last-Modified” and “ETag”.

Time-based cache headers

The “Cache-Control” header sets a value that enables caching, the browser will cache the resource for as long as specified. Lack of “Cache-Control” header the browser will re-request same resource on each subsequent request. List of all the available”Cache-Control” tokens and their meaning:

  • private: resources can only be cached by the end-client’s browser. Any intermediate proxies are bypassed.
  • public: resources can be cached by browser and any intermediate proxies.
  • no-cache: should not be cached anyway
  • no-store: can be cached in memory. However, it should not be stored on disk.
  • no-transform: the resource should not be modified (Any intermediate proxies can’t alter resource such as shrinking images etc…)
  • max-age: how long the resource is valid (measured in seconds, 1 year is 31536000 seconds)
  • s-maxage: same like max-age whereas this value is just for non clients

Example:
Cache-Control:private, max-age=31536000

“Expires” header sets a date from this date forward the browser will request a fresh copy of the resource. However, the browsers use local cached until then date is expired. If both “Expires” and “max-age” are set, then the “max-age” will override the “Expires” header.

Example:
Cache-Control:private
Expires: Tue, 19 Apr 2016 21:28:12 GMT

“Last-Modified” are conditional requests where the browser may ask the server if the browser’s copy is the most recent. The browser will invoke a call across the network. The application will determine whether updated content should be returned or not. If the browser’s copy is up to date, then HTTP status of 304 (not modified) is returned with the empty body. The application sets the last modified time of a resource in the “Last-Modified”response header.

Example:
Cache-Control:public, max-age=31536000
Last-Modified: Tue, 19 Apr 2016 22:08:57 GMT

The next time the browser requests this resource it will only ask for the contents of the resource if they’re unchanged since this date using the “If-Modified-Since” header.

Example:
If-Modified-Since: Tue, 19 Apr 2016 22:08:57 GMT

If the resource hasn’t changed, the server will return with an empty body with the 304 response code.

“Etag” is content-based request that works in a similar way to the “Last-Modified” header except its value should be a unique hash of the resource. This allows the server to identify if the cached contents of the resource are different to the most recent version.

Example:
Cache-Control:public, max-age=31536000
ETag: “06218aba045a4506a192aac21e6aebac”

On subsequent browser requests “If-None-Match” request header is sent with the “ETag” value of the last requested version of the resource.

Example:
If-None-Match: “06218aba045a4506a192aac21e6aebac”

If the current version has the same “ETag” value, indicating its value is the same as the browser’s cached copy, then an HTTP status of 304 is returned with empty body.

Tell the browser to explicitly to not cache items:
In addition to “public” and “private” the “Cache-Control” header can specify “no-cache” and “no-store” which informs the browser to not cache the resources under any circumstances. Both values are required as IE uses “no-cache”, and Firefox uses “no-store”.

Implementing HTTP Caching in Java with JAX-RS:

package com.emrekoca.jersey;

import java.util.Date;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.springframework.stereotype.Component;

/**
*
* @author ekoca
*
* HTTP caching can speed up web applications. It’s standardized and
* well implemented in all modern browsers and results in a lower
* latency app and improved responsiveness.
*
* HTTP caching occurs when an item is fully cached for faster retrieval
* the next time the resource is required. The browser may choose to not
* contact the server at all and simply use its own local copies of web
* resources. There are many cache headers such as “Cache-Control”,
* “Expires”, “Last-Modified” and “ETag”
*/
@Component
@Path("/cache")
public class HttpCacheEndPoint {

@GET
public String message() {
return "Hello";
}

@GET
@Path("/expires")
public Response expires() {
String hello = "Open in new tab, it should load from cache";
final int CACHE_DURATION_IN_SECOND = 60 * 60 * 24 * 2; // 2 days
final long CACHE_DURATION_IN_MS = CACHE_DURATION_IN_SECOND * 1000;
long now = System.currentTimeMillis();
return Response.ok(hello).expires(new Date(now + CACHE_DURATION_IN_MS)).build();
}

@GET
@Path("/override")
public Response maxAgeOverrideExpires() {
String hello = "Max-Age will override Expires";
final int CACHE_DURATION_IN_SECOND = 60 * 60 * 24 * 2; // 2 days
final long CACHE_DURATION_IN_MS = CACHE_DURATION_IN_SECOND * 1000;
// Setting up cache control header in java
CacheControl cache = new CacheControl();
cache.setPrivate(true); // private
cache.setMaxAge(1);
long now = System.currentTimeMillis();
return Response.ok(hello).expires(new Date(now + CACHE_DURATION_IN_MS)).cacheControl(cache).build();
}

@GET
@Path("/max-age")
public Response maxAge() {
String hello = "hello";
// Setting up cache control header in java
CacheControl cache = new CacheControl();
cache.setMaxAge(30); // max-age; 30 seconds
cache.setPrivate(true); // private
return Response.ok(hello).cacheControl(cache).build();
}

@GET
@Path("/etag")
public Response eTag(@Context Request request) {
String hello = "ETag header in HTTP cache";
// Setting up cache control header in java
CacheControl cache = new CacheControl();
cache.setMaxAge(31536000); // max-age; see above
cache.setPrivate(true); // private; see above

EntityTag etag = new EntityTag(Integer.toString(hello.hashCode()));
// determine the current version has the same "ETag" value,
// the browser’s cached copy "Etag" value passed by If-None-Match header
ResponseBuilder builder = request.evaluatePreconditions(etag);

// did cached resource change?
if (builder == null) {
// resource is modified so server new content
// 200 OK status code is returned with new content
return Response.ok(hello).tag(etag).cacheControl(cache).build();
}

// the browser’s copy is the most recent
// 304 Not Modified status code is returned with empty body
return builder.cacheControl(cache).build();
}

@GET
@Path("/last-modified/{time}")
public Response lastModified(@PathParam("time") long time, @Context Request request) {
String hello = "Last-Modified in http response header > If-Modified-Since in http request header = 304 returned; currentTimeMillis:";
long now = System.currentTimeMillis();
ResponseBuilder builder = request.evaluatePreconditions(new Date(time));
if (builder == null) {
// if time > now then 200 OK returned
// Resource is modified so server new content
return Response.ok(hello + now).lastModified(new Date(System.currentTimeMillis())).build();
}
// if time < now
// HTTP Status Code: 304 Not Modified returned
return builder.build();
}

}
Advertisements

One thought on “HTTP Cache Headers

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s