demo · v138
Header trace
Three different ways to trigger a fetch — <link rel="prefetch">, speculation-rules prefetch, plain fetch() — sent through a Service Worker that records the exact request headers. The compatibility matrix shows what every browser does, with v138's addition highlighted.
Unflagged in Chrome 138.
link rel="prefetch" now sends Sec-Purpose: prefetch. Brings it in line with speculation-rules prefetch (which already did) so origin servers and analytics platforms can branch on a single header.
probing…
Compatibility matrix
request mechanism
Sec-Purpose: prefetch
Sec-Fetch-Dest
Purpose: prefetch (legacy)
<link rel="prefetch">
v138 NEW — yes
empty
legacy yes (until removal)
<script type=speculationrules> prefetch
yes (since v109)
empty
no
<script type=speculationrules> prerender
yes: prefetch;prerender
document
no
plain fetch()
no
empty
no
Request traces
Server-side branching
// e.g. Express / Cloudflare Worker
app.get('/article/:id', (req, res) => {
const isSpeculative = req.headers['sec-purpose'] === 'prefetch';
if (isSpeculative) {
// skip analytics, skip 'mark as read', skip rate limit increments
return renderArticle(req, res, {trackHit: false, useCachedSession: true});
}
return renderArticle(req, res, {trackHit: true});
});
What's happening
- Pre-v138, only speculation-rules prefetches sent
Sec-Purpose.link rel="prefetch"sent the olderPurpose: prefetchinstead — different name, different value space. - v138 unifies them:
link rel="prefetch"now also sendsSec-Purpose: prefetch. (The legacy header is still sent for backward compat.) - Origin servers and SWs can now branch on a single canonical header. The tracer above shows it live: trigger each mechanism, watch the recorded headers.