RFC 9211

The Cache-Status HTTP Response Header Field

πŸ“… published June 2022, from ietf

RFC 9211 πŸ’– The Cache-Status HTTP Response Header Field

Okay bestie, here are the deets πŸŽ€

  • Number: RFC 9211
  • Title: The Cache-Status HTTP Response Header Field
  • Author: M. Nottingham, Fastly πŸ’…
  • Category: Standards Track
  • Date: June 2022
  • ISSN: 2070-1721
  • Obsoletes/Updates: there are no Obsoletes or Updates lines in its header πŸ™Œ

Abstract ✨

HTTP caches keep sneaking extra header fields onto responses to explain what they did, but everyone does it their own ad hoc way πŸ™ƒ This spec gives us one standard mechanism for it that actually lines up with HTTP's caching model πŸ¦‹

Status of This Memo ✨

This is the official paperwork πŸ“œ It's an Internet Standards Track document. It's a product of the IETF, it reflects the consensus of the IETF community, it got public review, and the IESG approved it for publication. If you want more on Internet Standards, check Section 2 of RFC 7841. For the current status, any errata, and how to send feedback, go to https://www.rfc-editor.org/info/rfc9211. πŸ”—

Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved. ©️

This document is covered by BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info), as they stood on the publication date. Please read those carefully, because they spell out your rights and restrictions for this document. Any Code Components pulled out of this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions, and they come without warranty as described in the Revised BSD License. βš–οΈ

Table of Contents πŸ—ΊοΈ

   1.  Introduction
     1.1.  Notational Conventions
   2.  The Cache-Status HTTP Response Header Field
     2.1.  The hit Parameter
     2.2.  The fwd Parameter
     2.3.  The fwd-status Parameter
     2.4.  The ttl Parameter
     2.5.  The stored Parameter
     2.6.  The collapsed Parameter
     2.7.  The key Parameter
     2.8.  The detail Parameter
   3.  Examples
   4.  Defining New Cache-Status Parameters
   5.  IANA Considerations
   6.  Security Considerations
   7.  References
     7.1.  Normative References
     7.2.  Informative References
   Author's Address

1. Introduction ✨

Debugging is so much easier when you know what a cache actually did πŸ” That's why HTTP caches often tack header fields onto a response explaining how they handled the request. It's useful for humans and for automated tools alike. The problem is that what these fields mean is usually unclear, and both the semantics and the syntax change from one implementation to the next 😭

So this spec defines a brand new HTTP response header field called Cache-Status for exactly this job, with standardized syntax and semantics πŸ’œ

1.1. Notational Conventions ✨

Quick caps check πŸ”€ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be read as described in BCP 14 [RFC2119] [RFC8174], but only when they're written in all capitals, just like they are here.

This document borrows some terminology from Section 3 of [STRUCTURED-FIELDS] for its syntax and parsing: List, String, Token, Integer, and Boolean. It also uses terms from [HTTP] and [HTTP-CACHING]. 🧠

2. The Cache-Status HTTP Response Header Field ✨

The Cache-Status HTTP response header field tells you how caches handled that response and its matching request πŸŽ€ Its syntax follows [STRUCTURED-FIELDS].

The value is a List, and each member of that List is a cache that handled the request. The first member is the cache closest to the origin server, and the last member is the cache closest to the user. That last one could even be the user agent's own cache, if it adds a value 🧚

Caches decide when it makes sense to add this header field. Some add it to every response. Others only do it when they've been specifically configured to, or when the request includes a header field that switches on a debugging mode πŸ› For the related security stuff, see Section 6, and take it seriously πŸ™

Intermediaries SHOULD NOT append a Cache-Status member to responses they generate locally, even if they contain a cache. The exception is when the generated response is built from a stored response, like 304 (Not Modified) and 206 (Partial Content), which both come from a stored response. For example, a proxy that generates a 400 response because the request was malformed won't add a Cache-Status value, since the proxy made that response itself and the origin server didn't πŸ™…β€β™€οΈ

When a cache adds its own value to Cache-Status, it SHOULD preserve the existing field value. That way, you can debug the whole chain of caches that handled the request πŸ”—

Each List member names the cache that inserted it, and that identifier MUST be a String or Token. Depending on the deployment, it could be a product or service name (like "ExampleCache" or "Example CDN"), a hostname ("cache-3.example.com"), an IP address, or a generated string 🏷️

Each member can also carry parameters describing how that cache handled the request. Those parameters are OPTIONAL, but caches are encouraged to share as much info as they can πŸ’…

Here are the parameters this specification defines ✨

2.1. The hit Parameter

The hit parameter is a Boolean. When it's true, it means the cache satisfied the request. In other words, the request wasn't forwarded, and the response came straight from the cache 🎯

A response the origin originally produced, but the cache modified (say, a 304 or 206 status code), still counts as a hit, as long as the request didn't go forward, for example for validation βœ…

A response that was sitting in the cache but couldn't be used without going forward (because it was stale or partial) does NOT count as a hit πŸ™… Heads up, though: a stale response that gets used without going forward (say, because the origin server is down) can count as a hit πŸ€·β€β™€οΈ

hit and fwd are mutually exclusive. Only one of them should show up on each List member.

2.2. The fwd Parameter

fwd, when it's there, tells you the request went forward toward the origin. Its value is a Token that explains why πŸš€

These are the values defined to explain why the request went forward, from most specific to least specific:

  • bypass: the cache was set up to not handle this request at all πŸ™ˆ
  • method: the request method's semantics mean the request has to be forwarded.
  • uri-miss: the cache didn't have any responses matching the request URI.
  • vary-miss: the cache had a response matching the request URI, but it couldn't pick one based on this request's header fields and the stored Vary header fields.
  • miss: the cache didn't have any responses that could satisfy this request. Use this when an implementation can't tell uri-miss and vary-miss apart.
  • request: the cache could pick a fresh response, but the request's semantics (like Cache-Control request directives) didn't allow using it πŸ€”
  • stale: the cache could pick a response for the request, but it was stale πŸ₯€
  • partial: the cache could pick a partial response for the request, but it didn't have all the ranges that were requested (or the request was for the complete response) 🧩

The most specific reason the cache knows SHOULD be used, as far as it's possible to implement. See also Section 4 of [HTTP-CACHING].

2.3. The fwd-status Parameter

The fwd-status parameter is an Integer that tells you which status code (see Section 15 of [HTTP]) the next-hop server sent back for the forwarded request πŸ“¨ It only means something when fwd is present. If fwd-status isn't there but fwd is, it defaults to the status code sent in the response.

This one's handy for telling apart a next-hop server sending a 304 (Not Modified) for a conditional request from a next-hop server sending a 206 (Partial Content) because of a range request πŸ•΅οΈβ€β™€οΈ

2.4. The ttl Parameter

The ttl parameter is an Integer showing how much freshness lifetime the response has left (see Section 4.2.1 of [HTTP-CACHING]), as the cache calculated it. It's an integer number of seconds, measured as close as possible to when the cache sends the response header section ⏳ That includes freshness the cache gave the response itself through things like heuristics (see Section 4.2.2 of [HTTP-CACHING]), local configuration, or other factors. It may be negative, which means the response is stale πŸ˜΅β€πŸ’«

2.5. The stored Parameter

The stored parameter is a Boolean that says whether the cache stored the response (see Section 3 of [HTTP-CACHING]). A true value means it did πŸ’Ύ This parameter only means something when fwd is present.

2.6. The collapsed Parameter

The collapsed parameter is a Boolean that says whether this request got collapsed together with one or more other forward requests (see Section 4 of [HTTP-CACHING]) 🫧 If it's true, the response was successfully reused. If it's false, a new request had to be made. If it isn't present at all, the request wasn't collapsed with any others. This parameter only means something when fwd is present.

2.7. The key Parameter

The key parameter is a String that represents the cache key (see Section 2 of [HTTP-CACHING]) used for the response πŸ”‘ Heads up that this may be implementation specific.

2.8. The detail Parameter

The detail parameter is either a String or a Token. It lets implementations pass along extra info that the other parameters don't cover, like implementation-specific states or other caching-related metrics πŸ“Š

For example:

Cache-Status: ExampleCache; hit; detail=MEMORY

A detail parameter only means something to the cache that sent it. Even if a detail parameter from a different cache has the same value, it might not mean the same thing πŸ™ƒ

This parameter is intentionally limited 🫣 If an implementation's developer or operator needs to share extra info in an interoperable way, they're encouraged to register extension parameters (see Section 4) or define another header field entirely πŸ’–


3. Examples ✨

okay bestie, let's look at some examples πŸŽ€ first up is the most minimal cache hit ever:

Cache-Status: ExampleCache; hit

but a polite cache will give you a bit more info, like this 🫢

Cache-Status: ExampleCache; hit; ttl=376

a stale hit just has negative freshness, like this one πŸ₯€

Cache-Status: ExampleCache; hit; ttl=-412

whereas this is a complete miss πŸ’€

Cache-Status: ExampleCache; fwd=uri-miss

this is a miss that successfully validated on the backend server, and it's giving main character energy πŸ‘‘

Cache-Status: ExampleCache; fwd=stale; fwd-status=304

this is a miss that got collapsed with another request, which is so efficient 🧚

Cache-Status: ExampleCache; fwd=uri-miss; collapsed

this is a miss where the cache tried to collapse the request but couldn't. ngl that's a sad little story 😭

Cache-Status: ExampleCache; fwd=uri-miss; collapsed=?0

now for the layered stuff, which is my favourite part 🌈 this one is two separate caching layers. the cache closest to the origin already answered an earlier request with a stored response, and then a second cache stored that response and reused it later to satisfy the current request. two caches, one response, zero drama ✨

Cache-Status: OriginCache; hit; ttl=1100,
              "CDN Company Here"; hit; ttl=545

and this is a three-layer caching system, which is honestly iconic πŸ’… the one closest to the origin is a reverse proxy, and the response came from its cache. next up is a forward proxy that the network put in the middle. it forwarded the request because nothing was cached for that URI, the request got collapsed with others, and the resulting response was stored. last is the browser cache, closest to the user, where nothing was cached for the request's URI

Cache-Status: ReverseProxyCache; hit
Cache-Status: ForwardProxyCache; fwd=uri-miss; collapsed; stored
Cache-Status: BrowserCache; fwd=uri-miss

4. Defining New Cache-Status Parameters πŸ¦‹

okay so if you want to make a brand new Cache-Status parameter, you register it in the "HTTP Cache-Status" registry πŸ“

registration requests get reviewed and approved by a designated expert, per [RFC8126], Section 4.5. a specification document is appreciated but not required, so you don't have to write one, but it's a very nice thing to do 🌸

the expert(s) should think about these factors when they evaluate requests:

  • Community feedback
  • If the value is sufficiently well defined
  • Generic parameters are preferred over vendor-specific, application-specific, or deployment-specific values. If a generic value cannot be agreed upon in the community, the parameter's name should be correspondingly specific (e.g., with a prefix that identifies the vendor, application, or deployment).

registration requests should use the following template, no skipping the boring bits πŸ™ˆ

Name:  [a name for the Cache-Status parameter's key; see
   Section 3.1.2 of [STRUCTURED-FIELDS] for syntactic requirements]

Type:  [the Structured Type of the parameter's value; see
   Section 3.1.2 of [STRUCTURED-FIELDS]]

Description:  [a description of the parameter's semantics]

Reference:  [to a specification defining this parameter, if
   available]

see the registry at https://www.iana.org/assignments/http-cache-status for details on where to send registration requests πŸ’–


5. IANA Considerations πŸ’…

IANA has created the "HTTP Cache-Status" registry at https://www.iana.org/assignments/http-cache-status and populated it with the types defined in Section 2. see Section 4 for its associated procedures πŸŽ€

IANA also added the following entry in the "Hypertext Transfer Protocol (HTTP) Field Name Registry", which is defined in [HTTP], Section 18.4 ✨

Field name:  Cache-Status
Status:  permanent
Reference:  RFC 9211

6. Security Considerations πŸ’€

okay this section is serious, bestie, so pay attention πŸ₯Ί attackers can use the information in Cache-Status to probe how the cache (and other components) behave, and then infer what the people using the cache are doing. the Cache-Status header field may not create these risks on its own, but it can help attackers exploit them 😱

for example, knowing whether a cache has stored a response can help an attacker run a timing attack on sensitive data. yikes πŸ™ˆ

also, exposing the cache key can help an attacker understand changes to the cache key, which may help with cache poisoning attacks. see [ENTANGLE] for details πŸ•·οΈ

the underlying risks can be mitigated with a bunch of techniques, like encryption and authentication and not putting attacker-controlled data in the cache key. which one you need depends on the exact nature of the risk. and note that merely obfuscating the key does not mitigate this risk. you can't just hide it and call it a day πŸ™…β€β™€οΈ

to avoid helping these attacks, you can omit the Cache-Status header field, only send it when the client is authorized to receive it, or send sensitive information (e.g., the key parameter) only when the client is authorized. protect your bestie's data, periodt πŸ’œ


7. References πŸ“š

7.1. Normative References

7.2. Informative References

Author's Address 🌸

Mark Nottingham
Fastly
Prahran
Australia
Email: mnot@mnot.net
URI:   https://www.mnot.net/

that's the whole doc, bestie! πŸŒˆπŸ’– you made it through the Cache-Status spec πŸ‘‘

πŸ“¦ where this page comes from