Link proxy response
The HTTP equivalent of the HTML <link> element — declares relationships between resources, powers preload/prefetch hints, and enables API pagination.
What it does
Link is the HTTP header equivalent of the HTML <link> element. It declares relationships between the current resource and other resources — "this resource has a stylesheet at X", "the next page is at Y", "preload this font". It works at the HTTP layer, so it can express these relationships even for non-HTML resources (JSON APIs, binary files) and before the HTML body is parsed.
Syntax
Link: <url>; rel="<relation>"[; <param>=<value>]
Link: <url1>; rel="<rel1>", <url2>; rel="<rel2>"
Examples:
Link: </style.css>; rel="stylesheet"
Link: <https://example.com/page/2>; rel="next"
Link: </api/users>; rel="next"; title="Next page"
Link: <https://fonts.gstatic.com>; rel="preconnect"
Link: </fonts/myfont.woff2>; rel="preload"; as="font"; crossorigin
Common rel values
rel value |
Meaning |
|---|---|
preload |
Load this resource ASAP, high priority, current page needs it |
prefetch |
Load this resource for likely future navigation, low priority |
preconnect |
Establish TCP+TLS connection to this origin early |
dns-prefetch |
Resolve DNS for this origin early |
next |
The next resource in a sequence (pagination) |
prev |
The previous resource in a sequence |
alternate |
Alternative version (different language, format) |
canonical |
Canonical URL for this resource |
stylesheet |
A stylesheet for this resource |
author |
Author of this resource |
help |
Help documentation |
Performance hints: preload and prefetch
Link headers with rel="preload" are processed earlier than <link> tags in HTML because they're in the response headers — the browser sees them before it even starts parsing the HTML body.
Preload a critical font:
Link: </fonts/inter.woff2>; rel="preload"; as="font"; type="font/woff2"; crossorigin
Preconnect to a CDN:
Link: <https://cdn.example.com>; rel="preconnect"
Prefetch next page resources:
Link: <https://example.com/page/2>; rel="prefetch"
The as attribute is required for preload — it tells the browser what type of resource it is so it can set the right priority and CORS mode:
as value |
Resource type |
|---|---|
script |
JavaScript |
style |
CSS |
font |
Web font |
image |
Image |
fetch |
fetch()/XHR response |
document |
iframe/frame |
API pagination
The Link header is the standard way to communicate pagination in REST APIs. GitHub popularised this pattern:
HTTP/1.1 200 OK
Link: <https://api.example.com/users?page=2>; rel="next",
<https://api.example.com/users?page=10>; rel="last",
<https://api.example.com/users?page=1>; rel="first"
Clients parse this to discover pagination URLs without embedding them in the response body. Clean separation of hypermedia controls from data. Widely adopted in GitHub API, Stripe, and many other well-designed REST APIs.
HTTP/2 Server Push (deprecated context)
Link headers with rel="preload" were used to trigger HTTP/2 Server Push — a way for servers to proactively send resources before the browser asked for them. CDNs like Fastly and Cloudflare supported converting Link: </style.css>; rel="preload" headers into HTTP/2 PUSH frames.
HTTP/2 Server Push is now deprecated in Chrome (removed in Chrome 106) because it caused more harm than good in practice (over-pushing, wasted bandwidth, cache-ignorant pushes). The Link: preload header still works for browser-initiated preloading — just not for server push.
Laravel example
// Preload assets from a controller or middleware
return response()->json($data)->withHeaders([
'Link' => implode(', ', [
'</fonts/inter.woff2>; rel="preload"; as="font"; crossorigin',
'<https://api.example.com>; rel="preconnect"',
])
]);
// Pagination
$users = User::paginate(20);
$links = [];
if ($users->currentPage() > 1) {
$links[] = "<{$users->previousPageUrl()}>; rel=\"prev\"";
}
if ($users->hasMorePages()) {
$links[] = "<{$users->nextPageUrl()}>; rel=\"next\"";
}
return response()->json($users)->withHeaders([
'Link' => implode(', ', $links)
]);
Common mistakes and gotchas
Missing as on preload. Link: </script.js>; rel="preload" without as="script" gets lower priority and may not benefit from early loading. Always include as.
Missing crossorigin on font preloads. Fonts are always fetched with CORS. Without crossorigin on your preload hint, the browser fetches it twice — once for the preload (wrong mode) and once when the CSS requests it (correct mode). Always add crossorigin for font preloads.
Confusing preload and prefetch. preload is for resources needed by the current page — the browser fetches them immediately at high priority. prefetch is for resources likely needed for future navigation — fetched at idle time, low priority. Using preload for things you don't need immediately wastes bandwidth and can hurt performance.
Real-world examples
Page with critical fonts and CDN preconnect:
HTTP/1.1 200 OK
Link: <https://fonts.gstatic.com>; rel="preconnect"; crossorigin,
</fonts/inter-var.woff2>; rel="preload"; as="font"; type="font/woff2"; crossorigin
GitHub-style paginated API:
HTTP/1.1 200 OK
Link: <https://api.github.com/repos?page=3>; rel="next",
<https://api.github.com/repos?page=1>; rel="first",
<https://api.github.com/repos?page=50>; rel="last",
<https://api.github.com/repos?page=1>; rel="prev"
FAQ
Is Link header the same as HTML <link>?
Same semantics, different delivery. HTML <link> is parsed from the document body; Link header is delivered before the document body. Both use the same rel values and mean the same things. The header version is processed earlier, which is why it matters for performance hints.
Can I use multiple Link headers?
Yes — either as multiple Link header instances, or comma-separated values in one header. Both are valid. Most frameworks handle the list format automatically.
Does the Link header affect SEO?
rel="canonical" in a Link header is honoured by Google as a canonical signal, same as the HTML equivalent. rel="next" and rel="prev" were previously used by Google for pagination signals but Google deprecated that usage in 2019 — they now discover pagination through other means.
Fun fact
The Link header is older than the web itself — it was part of early SGML document format proposals in the late 1980s and carried directly into HTML and HTTP. The RFC formalising its semantics (RFC 8288) wasn't published until 2017, nearly 30 years after the concept was first used. During those 30 years, Link was implemented inconsistently across browsers and servers, which is why pagination APIs had to wait for GitHub to popularise the pattern before it became common developer knowledge. One header, three decades of standardisation.