Skip to content

cache: response "must-revalidate" ignored when request has "max-stale" — stale served without validation (RFC 9111 §5.2.2.2) #5508

Description

@jeswr

Bug Description

isStale() (lib/interceptor/cache.js:65-91) grants the request max-stale grace window without
consulting the stored response's directives. RFC 9111 §5.2.2.2: a cache MUST NOT use a stale
response with must-revalidate to satisfy any request without successful validation — this
overrides max-stale (§5.2.1.2 note) and also excludes it from stale-if-error (RFC 5861 §4).
proxy-revalidate (§5.2.2.8) has the same semantics for shared caches — which is the
interceptor's default type — and is currently parsed but never enforced.

Repro: store 200 Cache-Control: max-age=1, must-revalidate (+ ETag); wait >1s; request with
Cache-Control: max-stale=600 → observed: served from cache, zero origin contact
(undici 8.7.0, current main, Node 22); expected: conditional request to the origin.

Suggested fix

Skip the max-stale grace in isStale() when
result.cacheControlDirectives['must-revalidate'] (or proxy-revalidate with type: 'shared'),
and exclude such responses from the stale-if-error threshold in handleResult. ~5-10 LOC + tests.


Found during an agent-assisted HTTP-caching review for @jeswr; every claim reproduced on undici 8.6.0 (repo) and 8.7.0 (npm) on Node 22.23.1. Fix PR to follow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions