RFC 9213 Targeted Cache-Control

RFC 9213 Targeted HTTP Cache-Control directives. Operators address specific layers in the caching chain by setting parallel headers that share the Cache-Control directive grammar but apply only to caches matching the target. The well-known shapes:

Cache-Control: max-age=60 (user-agent cache) CDN-Cache-Control: max-age=3600 (every CDN class cache) Cloudflare-CDN-Cache-Control: max-age=86400 (CDN-specific override) Vercel-CDN-Cache-Control: max-age=86400 (CDN-specific override) Surrogate-Control: max-age=3600 (W3C Edge Architecture)

The same response can carry multiple targeted variants — a CDN that recognizes its operator-specific header uses that one; CDNs without a match fall back to CDN-Cache-Control; user agents apply only the plain Cache-Control. The framework treats every variant as the same RFC 9111 §5.2.2 directive grammar.

build({...}) emits a directive string for any of the targeted headers; the operator chooses which header name to set. parse() round-trips: decode an inbound directive list into a normalized object with numeric maxAge / staleWhileRevalidate, boolean flags (public / private / noStore / noCache / mustRevalidate / immutable), and the raw directives map for unknown / extension keys.

TARGETED_HEADERS lists the well-known header names the operator may set; an explicit allowlist instead of guessing prevents operators from emitting a malformed CDN-Cache-Control-X-Custom header that no cache will look at.

b.cdnCacheControl.build(opts) #

stable0.8.91
{
  maxAge:               number,   // max-age=N (user-agent + shared)
  sMaxAge:              number,   // s-maxage=N (shared caches only)
  staleWhileRevalidate: number,   // RFC 5861 §3
  staleIfError:         number,   // RFC 5861 §4
  minFresh:             number,   // request directive
  maxStale:             number,   // request directive
  public:               boolean,
  private:              boolean,
  noStore:              boolean,
  noCache:              boolean,
  mustRevalidate:       boolean,
  proxyRevalidate:      boolean,
  mustUnderstand:       boolean,  // RFC 8246
  noTransform:          boolean,
  immutable:            boolean,  // RFC 8246
  extensions:           object,   // raw key→value/true map for non-standard directives
}

Build a Cache-Control-style directive list string for any RFC 9213 targeted header. opts accepts the standard RFC 9111 §5.2.2 directives in camelCase (maxAge, sMaxAge, staleWhileRevalidate, staleIfError, mustRevalidate, etc.) — kebab-case keys (max-age, s-maxage, ...) also pass through unchanged for operators porting from existing header-building code.

Numeric directives accept non-negative finite integers; the primitive refuses negative / non-integer / Infinity / NaN inputs (the directive grammar requires delta-seconds). Boolean directives only emit when explicitly true; false / undefined omit the token.

Returns the directive list string ready to be assigned to any header in TARGETED_HEADERS. Caller is responsible for choosing which header name to set.

res.setHeader("CDN-Cache-Control", b.cdnCacheControl.build({
  public:               true,
  sMaxAge:              3600,
  staleWhileRevalidate: 60,
  staleIfError:         86400,
}));
// → "public, s-maxage=3600, stale-while-revalidate=60, stale-if-error=86400"

res.setHeader("Cache-Control", b.cdnCacheControl.build({
  private: true, maxAge: 0, noStore: true,
}));
// → "private, max-age=0, no-store"

b.cdnCacheControl.parse(headerValue) #

stable0.8.91

Parse a Cache-Control-style directive list (from any RFC 9213 targeted header) into a normalized object. Returns null for absent / empty / non-string input — operator code branches on null vs the populated shape.

Numeric directives are surfaced as camelCase number fields (maxAge, sMaxAge, staleWhileRevalidate, staleIfError, minFresh, maxStale); boolean directives as camelCase boolean fields. Unknown directives land in directives (a name → value map where boolean directives map to true and value-bearing directives map to the raw string).

Defensive parser: tolerates trailing semicolons, repeated whitespace, and unquoted-quoted-string values; refuses control characters in the header value (CR/LF/NUL/DEL header-injection shape) by throwing cdn-cache-control/bad-header-value. ASCII HT remains permitted (structural folding whitespace).

b.cdnCacheControl.parse("public, s-maxage=3600, stale-while-revalidate=60");
// → { public: true, sMaxAge: 3600, staleWhileRevalidate: 60, directives: {} }

b.cdnCacheControl.parse("private, no-store, x-foo=bar");
// → { private: true, noStore: true, directives: { "x-foo": "bar" } }

b.cdnCacheControl.isTargetedHeader(headerName) #

stable0.8.91

Returns true when headerName matches one of the well-known RFC 9213 targeted header names (case-insensitive). Operators auditing an inbound response's cache headers walk the response headers and call this to identify which directive lists were intended for which cache class.

b.cdnCacheControl.isTargetedHeader("CDN-Cache-Control");       // → true
b.cdnCacheControl.isTargetedHeader("cloudflare-cdn-cache-control"); // → true
b.cdnCacheControl.isTargetedHeader("Cache");                   // → false

Last updated 2026-08-08T16:39:49.652Z by seeder.