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:
- Allow
origin in the Vary allowlist alongside accept-encoding.
- 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.
What happens
getCachePolicyrefuses any response whoseVarynames anything other thanaccept-encoding. CDNs stampVary: Accept-Encoding, Originon 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
Originvaries 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:So allowing
Vary: Originwithout touching the key would let an entry fetched while rendering one origin be replayed to a request from a different origin, carrying the storedaccess-control-allow-originfor the wrong origin. For acrossorigin/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:
originin the Vary allowlist alongsideaccept-encoding.Originheader 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
Varyvalues are ever accepted. Keying onOriginunconditionally 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.