demo · v141
Sec-Purpose Migration
Chrome 141 stops sending the legacy Purpose: prefetch header on prefetches and prerenders. Servers that branch on the old header — analytics-bypass, prefetch-friendly caching, alternate content delivery — need to switch to the spec'd Sec-Purpose header. This concept lets you paste your server middleware and check whether it'd still work.
Purpose: prefetch, prefetched requests will look like real navigations and double-count, blow your cache, or worse trigger personalisation. The spec'd Sec- prefix prevents JavaScript-set forgeries.
before 141 — three browsers, three headers
GET /article HTTP/2 purpose: prefetch # chrome (lowercase) x-moz: prefetch # firefox (safari: nothing)
after 141 — spec-aligned
GET /article HTTP/2 sec-purpose: prefetch # plain prefetch sec-purpose: prefetch;prerender # prerender variant
Paste your server's prefetch detection (Express middleware, nginx config, edge function) — the checker flags any old-header branches:
migration snippet
app.use((req, res, next) => {
const purpose = req.headers["sec-purpose"] || req.headers["purpose"] || req.headers["x-moz"];
if (purpose && purpose.includes("prefetch")) {
res.locals.isPrefetch = true;
res.locals.isPrerender = purpose.includes("prerender");
res.setHeader("Cache-Control", "private, max-age=60");
}
next();
});
why this angle
The Fetch spec change PR (whatwg/fetch#1576) was about interop, but the operational consequence sits with server operators. Sites that built analytics-suppression, prefetch-friendly caching, or alternate-content paths around the legacy headers won't immediately notice they've broken — prefetched requests just look like normal navigations, double-counting page views and busting per-user caches. The static checker here makes the migration visible.