教程·阅读约 2 分钟·
让 AI Agent 直接读你的网站:Accept: text/markdown 内容协商实战教程

让 AI Agent 直接读你的网站:Accept: text/markdown 内容协商实战教程

手把手教你把网站变成 AI Agent 友好:用 Accept: text/markdown 内容协商,让 Claude Code、Cursor 等工具直接读到干净的 Markdown。含 Caddy、Nginx、Next.js、Cloudflare 配置和验证方法。

原文来源:Accept Markdown — 用 HTTP 内容协商的 Accept: text/markdown 约定,让 AI Agent 从你的网站直接读到干净的 Markdown,而不是充满导航和脚本的 HTML。

越来越多的 AI 工具(Claude Code、Cursor、Copilot)自带"读网页"的能力,但它们的抓取器拿到的是给浏览器看的 HTML:导航、脚本、样式、广告位全混在一起。你的文章本身可能只有几千字,HTML 却有好几十 KB。本文教你用 HTTP 标准里的内容协商(Content Negotiation)机制,让 AI Agent 直接拿到干净的 Markdown。

为什么值得做

内容协商的含义是:同一个 URL,服务器根据请求头里的 Accept 字段决定返回什么格式。给 AI Agent 返回 Markdown 有三个实打实的好处:

  • 省 Token:Markdown 去掉导航、样式、脚本后体积可能只有 HTML 的零头,Agent 的上下文窗口留给你的正文,而不是 DOM。
  • 检索质量更高:RAG 管道做嵌入时不会被广告、相关推荐、弹窗干扰,文本的信噪比高得多。
  • 首字延迟更低:要抓的字节少了,解析也快了,模型更快开始思考。

这个约定不是厂商私有方案:媒体类型定义在 RFC 7763(text/markdown),协商语义来自 RFC 9110(HTTP 语义),都是标准协议。

—— 广告 ——

三步快速开始

任何栈的实现都逃不出这三步,总共不到 20 行配置。

第一步:检测 Accept 头。 请求进来时读 Accept,如果它把 text/markdown 排在 text/html 前面,就返回 Markdown。注意不要做子串匹配——要按 q 值排序、按媒体类型精确度分胜负,或者直接用现成的解析库。q 值规则要理解对:Accept: text/markdown;q=0.9, text/html;q=0.8 表示两个都接受但更想要 Markdown;q=0 表示明确拒绝;同权重时更具体的媒体类型胜出。

第二步:返回对应的表示。 响应里做两件事:Content-Type 设为 text/markdown; charset=utf-8(或 text/html),同时设置 Vary: Accept,让缓存按请求头区分版本。如果客户端明确要求了你不支持的类型,返回 406 Not Acceptable,而不是悄悄回退。

第三步:用 curl 验证。

code
curl -sI -H "Accept: text/markdown" https://你的域名/文章
# 应该看到 Content-Type: text/markdown 和 Vary: Accept

把 Accept 换成 text/html 再请求同一个 URL,应该拿到 HTML。检查你的实现是否完整,就看四条:Markdown 表示是否只在 Accept: text/markdown 时返回、是否设置了 Vary: Accept、不支持的类型是否返回 406、q 值是否被正确处理——acceptmarkdown.com 的在线检测工具可以帮你对任意 URL 跑这四项检查。

关键陷阱

Vary: Accept 不能省。 没有它,CDN 和浏览器缓存会把 Markdown 和 HTML 当成同一个资源,第一个来的人拿到什么,后面所有人都拿到什么。Markdown 版本被浏览器缓存、或者 HTML 版本被 Agent 缓存,都是灾难。Vary 是必要条件,但不同 CDN 的处理还有各自的坑——Cloudflare、Fastly、Vercel 都有各自的怪癖,上线前要分别测过。

406 别乱用。 只有客户端明确要求了你给不了的东西才返回 406。浏览器一般发 Accept: text/html,application/xhtml+xml,...,这时候你当然要回 HTML。最常见的错误是协商逻辑写得太激进,把正常浏览器请求也 406 了。

Markdown 从哪来。 三种做法:内容本来就是 Markdown 源(直接用,比如很多静态博客);构建时双渲染(同一份源同时生成 HTML 和 Markdown);运行时把 HTML 转 Markdown。选哪种取决于你的内容管线。Cloudflare 用户还有个零配置选项:站点在 Cloudflare 后面时,开"Markdown for Agents"开关,边缘层直接完成协商,源站零改动。

各栈配置速查

  • Caddy:用命名匹配器(named matchers)最简洁,预渲染的 .md 文件在同一 URL 下给 Agent,HTML 给浏览器:

    code
    example.com {
        @markdown header Accept text/markdown
        handle @markdown {
            rewrite * /{path}.md
            root * /var/www/site
            file_server
        }
        handle {
            root * /var/www/site
            file_server
        }
    }
  • Nginxmap + try_files 实现,Markdown 和 HTML 从同一个 URL 分发。

  • Next.js(App Router):写一个 middleware 做 Accept 解析,设置 Vary 和 406 语义,注意区分 Vercel、Cloudflare 和自托管 Node 的部署差异。

  • Astro / SvelteKit / Nuxt:各自有 middleware 或 handle hook 的写法,SvelteKit 可以用路由端点直接返回 Markdown,并带上 Link: rel="alternate" 广告备用版本。

  • Express / Django / Rails / Go:都有一行中间件或 respond_to 的写法——Rails 甚至内置了 text/markdown MIME 类型和 markdown 渲染器,还会自动设置 Vary: Accept

  • WordPress / Discourse:Roots 团队提供了 post-content-to-markdown 和 discourse-to-markdown 插件,装完即用。

哪些 AI Agent 支持

根据 acceptmarkdown.com 的实测矩阵(最后更新于 2026-06-22):Claude Code、Copilot Chat、Copilot CLI、Cursor、OpenCode、OpenClaw 会发送 Accept: text/markdown,开箱即用;Codex CLI 是部分支持——它先抓 HTML,再解析 <link rel="alternate" type="text/markdown"> 找 Markdown 兄弟版本,所以如果你用 Codex,记得在页面 <head> 里加上这个 link;ChatGPT 网页浏览、Gemini CLI、Perplexity、Devin、Cline、Grok 目前只抓 HTML,协商对它们无效。矩阵是站点作者逐个实测出来的,验证方法公开在页面上,Agent 行为随版本变化,你可以随时用自己的服务器复测。

想验证自己的站点:先在访问日志里加上 Accept 字段(Nginx 用 log_format$http_accept,Caddy 的 JSON 日志默认就带),然后让某个 Agent 去抓你的一篇文章,回日志里 grep 那篇文章的 URL,看 accept 字段里有没有 text/markdown

对独立开发者来说,这可能是成本最低的"AI 友好化"改造:你的内容本来就在,只是多一个按请求头分发的副本。改完之后还可以顺手在 <head> 里加上 <link rel="alternate" type="text/markdown">,给 Codex CLI 这类走备用链接的 Agent 指路。等 AI 流量占比越来越高,这个提前量会越来越值。

分享到
微博Twitter

© 2026 四月

原文链接:https://www.aprilzz.com/tutorials/serve-markdown-to-ai-agents