An Explanation of Caching

What are Caches?

At a high-level, there are two popular types of caching:

  1. In-app caching (APC, Memcache, Redis, other memory stores)
  2. HTTP caching - proxy, gateway, and private

In-App

In-app caching lives in your infrastructure and interacts with your application code. It allows you to store objects in memory for quick retrieval. It takes action when a request makes its way to your server. Your code checks your in-app caches and determines if it should use them to retrieve data in order to fulfill the request.

HTTP

HTTP Caching is mostly done by third-parties. They can potentially save requests from ever reaching your server by responding to a request itself.

  1. Proxy - A proxy cache is a public, shared cache often employed by an ISP or large corporation. Because they are employed at a high-level, they can (and do) cache thousands of various websites.
  2. Gateway - Similar to in-app caches, Gateway caches live as part of your infrastructure. They sit in front of your web-server and act much in the same way of proxy servers - except they are for your application(s) only. They are technically "reverse proxies".
  3. Private - Private caches are caches that are unique to a specific user. They live on the client-end. Your browser is a private cache; it will cache responses unique to the sites you visit.

Making a response (web page, api-response, etc) cacheable is part of the HTTP cache mechanism. Which cache mechanisms you use depends on your use case.

Note: I'll use the term origin server a lot. This is your web-server - the one that has your application and static assets, such as images, javascript and css files.

Types of HTTP caching

The HTTP spec defines 2 methods of HTTP caching:

  1. Validation - save bandwidth by not having an origin server reply with a full message body (your origin server returns a header-only response)
  2. Expiration - save round-trips to the origin server - a cache can potentially serve a response directly, saving the origin server from even knowing about the request

Validation Caching

Done with if-* headers (If-Match, If-Modified-Since, and so forth), validation caching is primarily used for 2 things (most useful for an API, in my opinion).

  1. Conditional GET requests - a server can tell the request 'nothing has changed since you last checked'. This is good for mobile APIs where the bandwidth of re-sending a message body can be saved via conditional requests.
  2. Concurrency Control - in a POST or, more likely, PUT request, a server can check if the resource being updated was changed since the requester last checked (solves the Lost Update Problem). This is good for APIs with a lot of writes (updates) to resources.

Expiration Caching

Done with Expires, Cache-Control, Date and related headers, expiration caching can aid in caching a response for the next user (or even for one specific user) on subsequent page loads, saving your server(s) from traffic load

  1. If you have a Gateway cache such as Varnish, you can potentially cache responses to end points per user A gateway cache gives you a lot of cache control since its part of your stack.
  2. Using the Cache-Control header to set a response to 'public' will allow Proxy and Gateway caches to cache your site content
  3. Requests behind authentication and/or SSL are usually not cached. You may be able to with a Gateway cache, or with a private cache (aka, your client can figure out caching based on your expiration headers).

Note: Validation caching typically over-rides Expiration caching. If you use Validation caching, and the cache (such as your browser!) supports it, the cache will likely ignore the expiration directives. This means all requests will make its way to the origin server. You likely want to disable ETags for your static assets (js, css, images) and selectively use it for your application responses.