你的博客该不该给 AI 一份 markdown

·约 9 分钟·修订于 2026年9月24日

一个 AI agent 抓取你的网页时,看到的东西和你看到的不一样。它看到的是两万个 token 的导航栏、页脚和 <div> 嵌套,而正文可能只占其中百分之几。

这不是比喻。Cloudflare 实测自己的文档:同一页 HTML 要 16,180 token, markdown 版只要 3,150 token——省 80%,内容一模一样。

HTTP 从 1.1 起就有解决这个问题的机制:内容协商。客户端在 Accept 头里说 「我更想要 markdown,不行的话 HTML 也行」,服务端照办。

问题是:博客圈几乎没人用。

谁在要 markdown#

拿七个常见 agent 去请求 httpbin.org/headers, 看它们各自发出的 Accept 头。结果是这样的:

Agent要 markdown 吗Accept 头
Claude Code是text/markdown, text/html, */*
Cursor是text/markdown, text/html;q=0.9, …
OpenCode是text/markdown;q=1.0, text/x-markdown;q=0.9, …
OpenAI Codex否text/html, application/xhtml+xml, …
GitHub Copilot否text/html, application/xhtml+xml, …
Gemini CLI否通配符
Windsurf否通配符

七个里三个主动要。而这七行数据本身就是一份测试用例——见 内容协商 里关于怎么把它钉成回归测试的部分。

一个安静失败的实现细节#

Claude Code 那一行值得单独说。

它发的是 text/markdown, text/html, */*——没有 q 值。在 HTTP 里, 缺省 q 值等于 1.0,于是 markdown 和 html 两个媒体范围并列。

这里要说清楚:RFC 并没有规定并列时怎么打破平局。 RFC 9110 §12.5.1 规定的是「媒体范围可以被更具体的范围覆盖,更具体的优先」—— 它按具体程度排序(text/plain;format=flowed → text/plain → text/* → */*), 而对 q 值相同的两个类型,规范只说它们是 equally preferred,没有给顺序。

所以「靠前的优先」是本项目的实现选择,不是规范要求。

但如果实现里写的是严格的 > 比较:

if (markdown.q > html.q) return 'markdown';
return 'html';

那么 1.0 > 1.0 为假,判定落到 HTML 上。Claude Code 永远拿不到 markdown, 而服务端不报错、不警告、日志里什么都没有。 你只会觉得「这功能好像没用」。

本项目的做法是并列时比顺序(如上所述,这是选择而非规范要求):

if (markdown.q !== html.q) return markdown.q > html.q;
return markdown.index < html.index;

这类 bug 的可怕之处在于它不会以错误的形式出现,只会以「没效果」的形式出现。

为什么现成的工具都不太行#

Astro 生态里已经有两个包在做这件事,但都停在半路:

  • astro-markdown-for-agents:在构建期生成 .md 文件,但协商只在 dev server 里生效。 它的 README 直说:「不包含托管平台运行时垫片;静态托管环境下的服务时协商 应由使用方自行实现。」也就是说上生产就没了。
  • astro-markdown-export:把源 .md 复制进产物目录,完全不做协商。 要访问 /slug/index.md 才拿得到,而且导出的是未渲染的源文件。

两者的周下载量分别是 106 和 17。而主流 Astro 集成是三十万到两百万。 这不是「已经解决了只是没人知道」,是「有人碰过就放弃了」。

难在哪#

难的不是算法——算法上面已经写完了,四十行。

难的是静态托管不解析请求头。静态站就是一堆文件,CDN 把文件原样吐出来, 它不会因为你 Accept 写了什么就换一个文件给你。所以你需要一层边缘函数。

这也是为什么 Cloudflare 的 Markdown for Agents 要 Pro 及以上套餐—— 它是在边缘替你做了转换。而 Vercel 的实现只在自己的平台内生效。 跑在免费静态托管上的博客,两头都够不着。

出路是:构建期生成 .md 孪生文件,再用一层几十行的边缘函数做选择。 转换在构建期完成(零运行时成本),边缘只负责「给哪个」。 Cloudflare Pages Functions、Netlify Edge Functions、Vercel Middleware 的免费额度都够用。

两件必须做对的事#

一、Vary: Accept 不能省。 少了它,CDN 会把 markdown 版本缓存下来 发给浏览器——用户打开博客看到一坨纯文本。这是个会让人当场关掉标签页的 bug, 而它只在缓存命中时出现。

二、*/* 不算「想要 markdown」。 Gemini CLI 和 Windsurf 只发通配符, 意思是「给什么都行」。如果把通配符也算作 markdown 偏好,这两家会被塞 markdown——它们本来接受 HTML,你硬给别的格式,属于自找麻烦。

顺带说一句 llms.txt#

你可能想问为什么不提 llms.txt。因为它的实际效果被严重高估了:

  • Google 明说不需要。 Search Central 的 AI 功能文档里写着:

    You don’t need to create new machine readable files, AI text files, or markup to appear in these features.

    注意原文说的是「不需要」而不是「不支持」——不做也不会因此缺席, 但 Google 也没说它有害。 (原文)

  • 多个独立实验都没测到可归因的效果。

这组数字曾经被删掉过,2026-09-24 找回来了。 这里原先写 「Ahrefs 2026 年 5 月实测 137,210 个域名:97% 的 llms.txt 从未被请求过」。 上一轮我按几种常见路径试 ahrefs.com/blog/* 全部 404、Ahrefs 自己的 sitemap 里没有收录博客,于是把整组数字删了,并写明「查不到就不该留着」。

那个删除是对的推理、错的结论:原文确实存在,地址是 https://ahrefs.com/blog/llmstxt-study/,此前只是没试中路径。 2026-09-24 重新抓取原文逐字核对,数据全部对得上:

  • 样本:Ahrefs Web Analytics 中2026 年 5 月获得流量的全部 137,210 个域名 (方法学章节原文:all 137,210 domains ... that received traffic in May 2026);
  • 其中 28% 发布了 llms.txt;
  • 这些文件的 97% 在 2026 年 5 月零请求——97% of those files received zero traffic in May 2026;
  • 有请求的那 3% 里,96% 的请求来自 bots。

发布日期与数据窗口是两件事:原文发布于 2026-06-15, 统计的是 2026 年 5 月。先前的写法把两者混成了「2026 年 5 月实测」。

留着一个可点开的来源,比留着一句「查不到」更有用; 但边界也要跟着一起写:这是对 llms.txt 根文件的 HTTP 200 探测, 不是对「AI 有没有读到内容」的观测——被请求的 3% 里,77% 的 bots 并不是 AI 工具。

它零成本、无下行风险,而且确实有 agent 会抓(Claude Code 抓取 llms.txt 的频率高于任何 AI 搜索机器人),所以本站在生成它。但把它当卖点是误导。

真正的杠杆是干净的内容协商和语义化的 HTML——后者对静态站来说是白送的。

参考文献#

  • RFC 9110 §12.5.1(Accept 头)、 RFC 7763(text/markdown 媒体类型)
  • Cloudflare, Markdown for Agents —— 正文里「16,180 → 3,150 token、省 80%」那组数字出自这里。 测量条件是 Cloudflare 拿自己的文档做的,本项目没有复现它; 它说明的是「干净 HTML 转 markdown 能省多少」,不是本站的节省率 (本站自己的数字在 内容协商 末尾,且是启发式估算,那里附了真实词表的对照)
  • Google Search Central, AI features and your website —— 正文里「Google 明说不需要」那段引文出自这里
  • Ahrefs, We Analyzed 137K Sites: 97% of llms.txt Files Never Get Read, 2026-06-15 发布、数据窗口为 2026 年 5 月 —— 正文里 137,210 / 97% / 96% 那组数字出自这里。 测量条件是 Ahrefs 拿自己的流量数据做的,本项目没有复现它; 它观测的是「根文件有没有被请求」,不是「AI 有没有读到内容」—— 被请求的那 3% 里 77% 的 bots 并不是 AI 工具
  • Ahrefs 那组数字原先作为一条独立引用,2026-09-23 被移除。 2026-09-24 找到了原始地址并已恢复(见上一条)—— 那一轮的删除是对的推理、错的结论:ahrefs.com/blog/* 几个常见路径全 404 只证明了「我按这几个路径没找到」,不等于「原文不存在」。 一个找不到的路径不是不存在的证据。

勘误(2026-09-22 / 09-23 / 09-24):这几条原先都是纯文本、点不开,已核实的补了链接; 09-23 那一轮把 Ahrefs 那条当作「核实不到」移除了,09-24 又找回来了—— 因为原文一直存在,只是当时试的路径没命中。 三轮下来值得记住的是:「我没找到」和「它不存在」之间隔着一整条推理链, 跳过那条链就会把一次失败的检索固化成一条结论。

这一页也有 markdown 版本:/letterpress/markdown-for-agents.md
直接打开上面的 .md 链接即可读取;在支持内容协商的部署平台上, 也可以对页面 URL 发送 Accept: text/markdown。