http link header 实测:27 个首页里 9 个在发,0 个用在 Google 写明的那两种用法上

http link header 是 link 元素搬到响应头里的写法。2026-09-11 读了 27 个首页:9 个在发,合计 39 条,13 条 preload、11 条 preconnect、5 条指向 /.well-known/api-catalog;Google 文档写明会从这个头读的 canonical 和 hreflang 一条都没有,把它作为 103 提前发出去的只有 1 个站。

抓取与收录8 分钟读完2565 次阅读
http link header 实测:27 个首页里 9 个在发,0 个用在 Google 写明的那两种用法上

实测 · 2026-09-11 · 30 个域名 · 27 个可读 · 单次抓取

样本 / 口径:仍是 2026-08-15 起在用的那 30 个站,2026-09-11 各请求一次首页,桌面 Chrome UA、HTTP/2、跟随跳转、不执行 JavaScript。服务器发回的每一个头块都留了下来,包括 103 这种中间响应;统计只看最终 URL 那一块。canva.com、medium.com、stackoverflow.com 对我们返回 403,纳入统计的是 27 个。

http link header 就是 <link> 元素搬到响应头里的写法:一个 URL、一个关系名、几个可选属性,在 HTML 之前先到。27 个首页里有 9 个在发,合计 39 条:13 条 preload、11 条 preconnect、5 条指向 /.well-known/api-catalog。而 Google 文档里写明会从这个头读的两种关系——canonical 和带 hreflangalternate——一条都没有。

先看一个真实的例子

搜这个词的人多半是想看它长什么样,所以把 www.cloudflare.com 当天的最终响应原样放在这里。一个 Link 字段里塞了 10 条,逗号分隔,每条是「尖括号里的 URL + 分号后的参数」:

link: <https://www.cloudflare.com/.well-known/agents.json>; rel="api-catalog",
      <https://www.cloudflare.com/.well-known/webmcp.json>; rel="service-desc",
      <https://www.cloudflare.com/openapi.json>; rel="service-desc",
      <https://www.cloudflare.com/llms.txt>; rel="service-doc",
      <https://www.cloudflare.com/sitemap.xml>; rel="sitemap",
      </fonts/Kunst%20Grotesk%20Regular.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin,
      </fonts/Kunst%20Grotesk%20Medium.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin,
      <https://ot.www.cloudflare.com>; rel=preconnect; crossorigin,
      <https://imagedelivery.net>; rel=preconnect; crossorigin,
      </static/hero-poster.avif>; rel=preload; as=image; type="image/avif"; fetchpriority=high

前五条是给机器看的「目录在哪」,后五条是给浏览器看的「先去拿这些」。这一个字段里同时装着这个头今天的两种用法,下面的数字就按这两种分。

怎么测的

一次请求、从外部、不登录任何账号。所有数字只来自跟随跳转之后那个最终响应上的 Link 字段:在每个 < 前面的逗号处切开,取每一段的 rel 参数,数。然后把头里指到的每一个 URL 都再取一遍,看被广播出去的东西存不存在。

# 一个域名的全部头块,服务器发 103 的话也一起吐出来
curl -s -o /dev/null -D - --http2 -L -A 'Mozilla/5.0 (Macintosh) Chrome/126.0' https://example.com/ \
  | grep -iE '^(HTTP/|link:)'

# 一行一条
curl -sIL -A 'Mozilla/5.0 (Macintosh) Chrome/126.0' https://example.com/ \
  | grep -i '^link:' | sed 's/, *</\n</g'

四条局限。我们没有读 HTML,所以一个把同样的 preload 写成页面里 <link> 元素的站,在这里算作「没发」——头和元素是两条通道,本次只读了一条。第二,首页只是一个网址,这个头经常是按路由配的。第三,结果和客户端有关:103 是服务器可以只对浏览器发的东西,那 26 个没发的站会不会对 Chrome 发,从外面判不了。第四,我们故意用了 curl:Python 标准库的 HTTP 客户端会把 1xx 响应悄悄吞掉,拿它写的脚本会报 0。

27 个首页里 9 个在发 http link header

这个头不常见,而发了的站在用它干两件完全不同的事。3 个站拿它发性能提示,5 个站拿它告诉机器自己的 API 目录在哪,Cloudflare 两件一起干。面板上唯一的 WordPress 站发的是 WordPress 默认带的那条 REST API 指针。

首页条数头里的关系名
www.cloudflare.com10preload 3 · preconnect 2 · api-catalog 1 · service-desc 2 · service-doc 1 · sitemap 1
webflow.com9preconnect 7 · preload 2
nextjs.org8preload 8
railway.com4api-catalog 1 · alternate 2 · sitemap 1
vercel.com3api-catalog 1 · ai-catalog 1 · agent-skills 1
www.framer.com2preconnect 2
www.netlify.com1api-catalog 1
supabase.com1api-catalog 1
techcrunch.com1https://api.w.org/ 1

另外 18 个一条 Link 都没发:arstechnica、bbc、figma、github、gitlab、mozilla、notion、nytimes、react.dev、reddit、shopify、slack、stripe、substack、theverge、wikipedia、wired、news.ycombinator。三个 403 响应和中间的跳转响应里也都没有。

39 条各指向什么

按关系名数,这个头在面板上主要还是一条性能通道,旁边长出了一条发现通道。preload 加 preconnect 占 39 条里的 24 条,但只来自 3 个站;api-catalog 每站一条、共 5 个站,是这里铺得最开的一种关系。

关系名站数条数指向什么
preload313字体 4、SVG 标志 6、CSS 2、AVIF 首屏海报 1
preconnect311资源 CDN、Google Fonts、同一家第三方的 4 个主机
api-catalog55/.well-known/api-catalog(4)或 /.well-known/agents.json(1)
sitemap22/sitemap.xml
alternate12/index.md(text/markdown)、/llms.txt(text/plain)
service-desc12webmcp.jsonopenapi.json
service-doc11/llms.txt
ai-catalog11/.well-known/ai-catalog.json
agent-skills11/.well-known/agent-skills/index.json
https://api.w.org/11WordPress 的 REST API 根
canonical00
alternate + hreflang00

preload 那一行有两处要多看一眼。nextjs.org 的 8 条全在 /_next/static/immutable/media/ 下面,两个字体、六个标志 SVG,这是框架吐出来的形状,不是人手写的。另外 HTML 标准对「哪些关系名允许从响应头生效」写得很死:preloadpreconnect 各有一套响应头处理步骤,而 iconmanifestmodulepreloadprefetchstylesheet 五种,标准原话是响应头形式的处理步骤 "are to do nothing"(HTML Standard §4.6.8,2026-09-11 访问)。面板上没有人发这五种。

Google 写明会读的两种,一条都没有

在 Google 的搜索文档里,我们能找到的这个头的用法只有两处,而且都是给放不下 <link> 元素的文件用的。规范网址那页写着 "you can return a rel="canonical" HTTP header to tell Googlebot what is the canonical URL for the non-HTML files",例子是 Link: <https://www.example.com/downloads/white-paper.pdf>; rel="canonical"Consolidate duplicate URLs,2026-09-11 访问)。多语言那页写着这个头 "is useful for non-HTML files (like PDFs)",格式是 Link: <url1>; rel="alternate"; hreflang="lang_code_1", …Tell Google about localized versions of your page,2026-09-11 访问)。

首页上本来就不该出现这两种,也确实没出现。这是对的结果,不是缺口:页面能用元素装下它们,面板上也确实装在那里——2026-08-19 我们读这批站的 HTML 时,28 个里 23 个在页面里声明了 canonical(canonical 标签的 28 个首页实测)。响应头这种写法真正的用武之地是 PDF、Google 例子里那个 .docx,以及任何你的 CDN 直接下发、没有 <head> 的文件。做独立站的人最常忽略的就是产品手册和规格书 PDF:它们有 HTML 版,却从来没被指回去。没写过的话,canonical 标签该留哪个网址那一章把响应头和另外三种信号按强弱排过。

5 个站用它告诉机器「API 目录在这」

api-catalog 这个关系名和 /.well-known/api-catalog 这个路径来自 2025 年 6 月发布的一份标准轨 RFC,自述目的是 "to facilitate automated discovery and usage of published Application Programming Interfaces"(RFC 9727,2026-09-11 访问)。RFC 规定目录 "MUST" 以 application/linkset+json 发布,并 "SHOULD" 带一个指向该 RFC 的 profile 参数。5 个目标我们全取了一遍,连同 9 个头指向的其余所有 URL。

目标状态Content-Type字节
vercel.com/.well-known/api-catalog200application/linkset+json; profile=…rfc9727405
www.netlify.com/.well-known/api-catalog200application/linkset+json1,004
railway.com/.well-known/api-catalog200application/linkset+json3,198
supabase.com/.well-known/api-catalog200application/linkset+json605
www.cloudflare.com/.well-known/agents.json200application/json4,768
www.cloudflare.com/.well-known/webmcp.json200application/json782
www.cloudflare.com/openapi.json200application/json1,218
www.cloudflare.com/llms.txt200text/plain17,176
railway.com/index.md200text/markdown13,181
railway.com/llms.txt200text/plain6,324
vercel.com/.well-known/ai-catalog.json200application/ai-catalog+json1,410
vercel.com/.well-known/agent-skills/index.json200application/json5,846
techcrunch.com/wp-json/200application/json780,962

被广播出去的 URL 全部存在。5 份目录里 4 份是 RFC 要求的格式,只有 Vercel 还带了那个 profile 参数。Cloudflare 那条 api-catalog 是异类:它指向一个 agents.json,首行声明的 schema 在 agentprotocol.ai,不是 RFC 的格式——一个相信关系名、按 Linkset 去解析的客户端会拿到另一种东西。Railway 是面板上唯一一个给首页广播了 markdown 分身的站,那个文件开头就写着 "Setup briefing for AI coding agents arriving at railway.com";这是我们在 给 AI 爬虫直接发 markdown 那篇里看到三个文档站在做的事,搬到了响应头这一层。

今天有没有哪个 AI 爬虫真的在读这些关系名,响应头回答不了。本次没测爬虫行为,我们也不打算猜。这 5 条头能证明的事更窄,但是真的:截至 2026-09-11,5 家基础设施公司决定把「这个站还提供什么」告诉机器的位置放在 HTTP 响应里,而不是 HTML 里。

只有一个站把它提前发了

103 响应的定义是 "indicates to the client that the server is likely to send a final response with the header fields included in the informational response"(RFC 8297,2026-09-11 访问),它存在的全部理由就是在页面还没准备好之前先把 Link 提示送出去。面板上只有一个首页发了它:www.cloudflare.com,103 里带 5 条——3 条 preload、2 条 preconnect——最终 200 重复这 5 条,再加 5 条发现类。其余 26 个可读的站,包括 preload 列表最长的 nextjs.org,都是把提示和最终响应一起发的,而那正是浏览器反正也会看到它们的时刻。

写在最终响应上的 Link 是贴在包裹上的便条;写在 103 上的同一条 Link,是包裹发出前打来的电话。

这对你意味着什么

先把自己的头读一遍,再决定要它干哪一件事。大多数站的首页两件都不需要;下面几条是它真正该出场的场合。

  • 给每一个有 HTML 版的 PDF、表格、导出文件在响应头里发 rel="canonical"——这是 Google 唯一写明的用法,也是给没有 <head> 的文件声明规范网址的唯一办法。
  • 翻译过的可下载文件,把整套 hreflang 作为响应头发在每一个文件上,包括被请求的那个文件自己,多语言那页要求的就是这样。
  • 对外有 API 的话,把目录放在 /.well-known/api-catalog、以 application/linkset+json 发布、用 rel="api-catalog" 指过去;已经这么做的 5 个站里 4 个和 RFC 对得上。
  • 别把 iconmanifeststylesheetprefetchmodulepreload 挪到响应头里。标准说这五种在响应头形式下什么都不做。
  • 别把最终响应上的 preload 当成提前提示。前面没有 103,它就是和 HTML 一起到的。

如果问题是「AI 爬虫到底能不能到我刚指过去的那个文件」,拿它去跑一遍 AI 爬虫可达性检查——指向一个爬虫被挡在外面的 URL 的发现链接,等于什么都没指。

常见问题

你们是怎么测的?

2026-09-11 对每个首页发一次 curl 请求,HTTP/2、桌面 Chrome UA、跟随跳转、用 -D - 把头全部倒出来,这样 103 那一块才不会丢。上面每个数字都是脚本从落盘的头块里重算的,没有一个是手输的。之后第二遍把头里指到的每个 URL 再取一次,记状态码、Content-Type 和大小。

http link header 是干什么用的?

它 "provides a means for serialising one or more links into HTTP headers"(RFC 8288,2026-09-11 访问)。实际上干三件事:浏览器在解析页面之前就能动手的性能提示、给装不下 HTML 的文件用的元数据,以及——新近出现的——给从不渲染页面的机器用的发现指针。

Google 会从响应头读 preload 或 preconnect 吗?

Google 的搜索文档没说,我们也没测。它写明会读的只有 canonical 和 hreflang。这个头里的其他内容,一律当成写给浏览器和愿意读它的那些 AI 爬虫的。

103 early hints 值得配吗?

只有当你已经有值得提前发的 preload,而且你的 CDN 愿意替你发 103 的时候——面板上做到这一点的是 27 个里的 1 个。对做 SEO 的人,老实的回答是我们没找到任何一份把 103 和抓取或排名联系起来的 Google 文档,所以把它归到页面性能那一栏,别归到搜索。

没有公开 API,要不要也加一条 api-catalog?

不要。这个关系名存在的意义就是指向一份目录,指向一个不是目录的页面等于告诉机器一件假的事。如果你有的是给人和模型看的文档,/llms.txt 和一份 markdown 分身——Railway 和 Cloudflare 发的那种——才是更贴的写法。

http link header 实测:27 个首页里 9 个在发,0 个用在 Google 写明的那两种用法上