Skip to content

Instantly share code, notes, and snippets.

@shoaibi
Last active December 13, 2015 16:36
Show Gist options
  • Select an option

  • Save shoaibi/dd8e17af16c20a91af60 to your computer and use it in GitHub Desktop.

Select an option

Save shoaibi/dd8e17af16c20a91af60 to your computer and use it in GitHub Desktop.
Few notes regarding how to work with HTTP Cache

General

  • Works only for safe HTTP methods, say like GET and HEAD, methods that shouldn't mutate application state.

Places where caching can occur

  • Browser (private and public)
  • Proxy (public)
  • Gateway/Reverse Proxy (public)

Caching Strategy

  • Is content reusable? No? no-store

  • Should cache be revalidated each time? no-cache

  • Cacheable by intermediate caches? public, else private

  • What is the age? max-age or s-maxage for shared

  • Does content have multiple representations based on request encoding, host or agent? Vary Header, say on Accept-Encoding, User-Agent

  • ETag, Last-Modified, Expires

  • Does content has references resources that could be updated? no-cache with small max-age on main with version suffices on files.

  • Minimize churn by stripping out frequently updated parts to separate end points

  • Page has fragments that should be refreshed more often? Say homepage should be cached for 60 minutes while sidebar for 5 minutes? Edge Side Includes(ESI)

  • Setting one year as cache age is a really bad idea. A day should be more than enough for most scenarios.

    If your servers can't stand to have your CDNs ask once a day if your profile picture has changed you should upgrade your servers.

unless a way to refresh stale assets is used (say version/hash appended file names, etc).

Caching Approaches

Expiration

  • Use Expires or Cache-Control's max-age.
  • Client only pings back when the content has expired.
  • Could serve stale content if expiration had a long value but minimize load on server

Validation

  • Last-Modified or ETag
  • Client pings back at each request to check if content has changed and server should response with HTTP304 with an empty body if it hasn't.
  • Adds load on server for each validation request, could be expensive depending on how the validation is verified
  • Server can provide overridden values in response even when responding as HTTP304.

Cache Invalidation:

  • Purge
    • Purge all variants of a url on gateway by issuing a PURGE request.
  • Refresh
    • Invalidate and pull latest version of a url. Doesn't impact variants
  • Banning
    • Invalidate multiple urls by using regex
  • Cache Tagging
    • Tag content so all urls containing a specific tagged content can be invalidated at the same time

Relevant Headers

ETag

  • Unique Representation of a resource
  • Client sends If-None-Match, with the ETag value from the version client has, to server when asking for validation.

Last Modified:

  • GMT Date time
  • Client sends If-Modified-Since when asking for validation

Cache-Control:

  • max-age
    • How long the content shall be cached for
    • Relative to Date, not Last-Modified
    • max-age=0 tells clients that they should revalidate the content.
      • Sending this from client to server would trigger revalidation on all intermediate shared caches
    • Overrides Expires header
  • s-maxage
    • Same as max-age but for shared cache endpoints and overrides max-cache
    • Usually greater than max-age
  • public/private
    • Should the content be cached at shared cache endpoints or not
  • must-revalidate
    • Regards max-age and only asks for revalidation after the max-age has passed.
  • no-cache
    • Client must revalidate each time (notice how it is must and not should as in the case of max-age=0) and serve from cache only if validation is successful
    • Does not regard the max-age, acts as must-revalidate with max-age=0
  • no-store
    • Client shouldn't cache anything at all (neither response contents nor any part of the actual request)
  • no-transform
    • Intermediate caches should not play with the content, say type guessing and encoding it to difference formats to save space.
  • proxy-revalidate
    • Same as must-revalidate but for intermediate caches
    • Validate each user only once between the intermediate cache and user agent, but each new user should revalidate back to the origin

A client should revalidate if showing a twitter feed, but same client must revalidate when doing a purchase.

Expires

  • Specifies an absolute GMT Date time as expiration value
  • Specification suggest to not set a value more than one year into future
  • Recommended to be used for static content while Cache-Control being opted for content which may have dynamic fragments (like css, js, xml, etc) fragments.

Vary

  • Allows intermediate caches to store different versions of a url based on the fields specified.
  • Assume that intermediate caches use a kv store to persist caches then these fields become part of the key.
  • A common example would be to do Vary: Accept-Encoding
    • Bad because not all browsers send same Accept-Encoding header for same type of response.
    • Generally we just want to save 2 versions of a response e.g. compressed and uncompressed.
    • It would be better to deal with this at gateway proxy with string matching against gzip/deflate instead.
  • Or to save different versions by user-agent: Vary: User-Agent if different kind of styles and scripts are loaded depending on the user agent.
    • This is also horrible. There are lots of different user-agents. Usually we just want 2 versions e.g. Mobile and desktop.
    • We should probably even do this. Why? Responsive Design.
  • Or slice by Referrer to show a welcome message in a popup: Vary: Referrer
    • Also horrible.
    • Say if your content is popular and linked by many sites and indexes by google (where each google result is a unique referrer) then it'd bloat cache.
    • A better way would be for the gateway cache to save just 2 copies, one for external links and one for internal.
  • Or Vary: Accept if responding with different response type(html, json, xml, yml, etc) depending on what client requests.
  • Vary: * is horrible. Use Cache-Control: private instead.
  • Vary: Accept-Encoding, User-Agent, Cookie, Referer. Don't even get me started on this one.

Pragma

  • Legacy
  • pragma: no-cache replaced by Cache-Control: no-cache

Misc

ESI Tags

  • Fully qualified urls to retrieve fragments of page.
  • Each fragment can then have its own set of cache rules, completely independent of the page that references it.
  • Gateway deals fragments as separate items and hence caches them separately.
  • For each incoming request gateway does following
    • Check if a suitable version exists in cache, if so, pull that up or fetches one from backend.
    • If the page has any ESI, those are treated in same way
    • Once all fragements are available, gateway merges them together into page and sends back the compiled response.
  • The whole check-or-fetch-merge-compile-send operation is done at gateway level and is transparent to application.
  • Application only needs to
    • Define ESI fragements
    • Ensure those fragments are available under specified urls
    • Use s-maxage instead of max-age as browser is provided with fully compiled resource. If max-age is set browser will cache the compiled page and we don't want that.

ETag vs Last-Modified

  • ETag is strong validator while Last-Modified is weak (default, server may change that)
  • RFC2616 recommends that a client must send ETag for any cache conditionals
  • In the case where both are provided, both are used.

What to send if the validation fails?

  • Act as if no cache-canditionals were specified, compile the whole response as a normal request.

How to test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment