Skip to content

Resource cache refuses Vary: Origin, so every CDN-served first-party asset is excluded; the URL-only key is what blocks allowing it #109

Description

@harper-joseph

What happens

getCachePolicy refuses any response whose Vary names anything other than accept-encoding. CDNs stamp Vary: Accept-Encoding, Origin on static assets as a matter of course once CORS is configured (Akamai does), so on such a deployment every first-party script and stylesheet is refused and the resource cache stores none of what it exists to store.

Measured on one storefront: 117 first-party script/stylesheet responses per render, ~9 MB, served max-age=7776000 (90 days) — all refused, all re-fetched on every render. What was left cacheable was third-party tags, which is its own problem (#108).

Why it is refused, and why simply allowing it would be unsound

Origin varies the CORS response headers (access-control-allow-origin), not the response body, so refusing on it is over-conservative. But the cache key is the URL alone:

keyFor(url) {
    return createHash('sha256').update(url).digest('hex');
}

So allowing Vary: Origin without touching the key would let an entry fetched while rendering one origin be replayed to a request from a different origin, carrying the stored access-control-allow-origin for the wrong origin. For a crossorigin/module fetch the browser would then block the asset — trading a silent miss for a silent breakage. The key is the actual blocker here, not the policy check.

Suggested fix

Two parts, and they only work together:

  1. Allow origin in the Vary allowlist alongside accept-encoding.
  2. Include the request's Origin header value in the cache key, so an entry is only ever replayed to a request from the origin it was fetched under.

Requests that send no Origin — a classic <script src> or <link rel=stylesheet> load, which is most of them — share an empty-origin variant, so the common case gains nothing to store and nothing to look up. Worst case is one extra variant per asset per rendered origin.

The general answer is a full multi-variant Vary implementation (record the varying header set under the URL, then key the variant), which is worth doing if more Vary values are ever accepted. Keying on Origin unconditionally gives the identical guarantee for the only header worth accepting today, in about ten lines.

Effect

With both parts, the same pages take ~40–50 cache hits per render against the first-party asset host instead of zero, and every stored entry verified byte-identical to a fresh origin fetch (53/53 on the deployment measured). Render wall-clock was unchanged — the win is origin requests avoided per render across a worker fleet, not latency.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions