> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 你的博客读者已经不是人：llms.txt 和 .md 才是它的入口
- URL: https://www.hubwiz.com/blog/blog-readable-by-ai-agents/
- Published: 2026-10-30T14:23:25.000Z
- Updated: 2026-10-30T14:24:59.000Z
- Description: AI 成了博客的第一个读者：.md、llms.txt、Accept 协商、显式 robots 与 IndexNow，全是文件。
- Author: admin
- Tags: AI 搜索, llms.txt, GEO, AI 爬虫, 静态站点

Deallocated Objects 的作者 Jake Marsh 有一次意识到，读他博客的"人"里有相当一部分不是人：Claude、ChatGPT、Perplexity 这一批，替某个正在提问的人来取一个页面。于是他花了一个晚上，让这个博客对它们好读一点——顺手也把搜索引擎那边该补的补上。

他的站是 nginx 托着的静态站，所以整篇的每条改法都落成两种东西：**构建时生成的文件，或者几行 nginx 配置。** 没有框架，没有插件，也没有"等 CMS 出功能"。

2026 年 10 月 24 日他把过程写了出来。下面是完整编译。

## 一、每篇文章都有一份 markdown

HTML 对 agent 来说噪音太多：导航、脚本、样式、埋点、模板壳子，真正的内容被埋在一堆标签里。所以他给每篇文章加了一份纯 markdown 版本——**在任何文章 URL 后面加 `.md`**：

```
https://deallocatedobjects.com/posts/the-art-of-the-reveal.md

```

每个文件开头有一小块 YAML frontmatter（标题、canonical URL、作者、日期、一句简短描述），然后是正文。老文章当年本来就是用 markdown 写的，所以构建过程只需要把那些文件清理一遍，并把它们的图片指向自托管的副本。

这一步的价值不在"多一种格式"，而在**同一份内容只维护一次**。正文是 markdown，HTML 和 `.md` 都是它的产物，不存在两份内容各自漂移的问题。

## 二、更讨巧的是内容协商：不用加 `.md`

如果客户端在 `Accept` 头里要 markdown，他就从**普通 URL** 直接返回 markdown——访问者不需要知道 `.md` 存在。

在 nginx 里，这是一个 `map` 加上每个 location 一行判断：

```nginx
# http context
map $http_accept $deallocated_md {
    default "";
    "~*text/markdown" ".md";
}

# server block
location ~ ^/posts/[^/.]+$ {
    if ($deallocated_md) { rewrite ^ $uri.md last; }
    try_files $uri.html =404;
}

```

验证一下：

```shell
curl -H "Accept: text/markdown" https://deallocatedobjects.com/posts/the-art-of-the-reveal

```

这里有两个细节比配置本身更重要。

**第一，每个响应都带 `Vary: Accept`。** 否则中间任何一层缓存都可能把手里的 HTML 交给 agent，或者反过来把 markdown 交给浏览器——而这个 bug 只在特定的缓存命中路径上出现，本地怎么测都是好的。

**第二，`.md` 响应同时发两个头。** 一个是 `Link`，指向对应的 HTML 页面并声明 canonical：

```
Link: <https://deallocatedobjects.com/posts/the-art-of-the-reveal>; rel="canonical"

```

另一个是 `X-Robots-Tag: noindex`。两个头配合，是为了让 Google 不把这些 `.md` 当成每篇文章的重复副本：**内容给 agent 用，权重归 HTML 页。** 只做密钥协商不声明 canonical，等于给自己制造一批重复内容。

## 三、llms.txt：告诉 agent 它在看什么

`/llms.txt` 是站点根目录下的一个纯 markdown 文件，作用是让 agent 知道"这是哪儿、怎么用"。他的那份包含：博客的一行简介、怎么拿到 markdown、feed 在哪里、希望文章被怎么引用，以及**全部文章的清单**——每篇一个 markdown 链接加一行摘要。

另外还有两个文件：

- `/llms-full.txt`：所有文章的全文塞进一个文件，约 225 KB；
- `/index.md`：首页的 markdown 版本。

这三个文件加上一行响应头就完整了：**每个响应都带一个指向 `llms.txt` 的 `Link` 头**，agent 不用猜路径就能找到它。

注意 `llms.txt` 目前在格式上还只是社区提案（起源于 2024 年 9 月），Google 没有表示会读它。但他这里的判断很朴素：它是一份文件，写一次，成本近乎为零，而它明确表达了"我希望被怎么引用"。

## 四、robots.txt：沉默等于允许，但他选择写明

`robots.txt` 里什么都不写，技术上等于"允许所有"。他更愿意显式表达：先放行所有人，然后把大的几家点名列出——`GPTBot`、`ClaudeBot`、`PerplexityBot`、`Google-Extended`，再加十来个同类 UA，最后加一行 `Content-Signal`：

```
User-agent: *
Allow: /
Content-Signal: search=yes, ai-input=yes, ai-train=yes

```

这条 `Content-Signal` 不是 robots.txt 的标准指令，而是一类"内容用途声明"的做法（由 CDN 厂商推动）：分别表示允许进搜索索引、允许作为 AI 的输入、允许用于训练。

他的取舍写得也很直白：**对一个博客来说这是对的选择，因为他想被引用；一个产品站可能会做不同的选择。** 这句话是整篇文章里最该被抄走的一句——robots 的取值不是"技术正确/错误"，而是"你要什么"。

## 五、Feed：Atom 保留，另加 JSON Feed

老的 Atom feed 还在 `/feed`，但每条 entry 现在都带摘要，以及一个 `text/markdown` 的 alternate 链接。他还加了一份 JSON Feed，每条 item 里带一个 `_markdown_url`——JSON Feed 允许以下划线开头的自定义键，所以这不算破坏规范。

Feed 这一层经常被当成"老古董"，但对 agent 来说它是最省事的结构化入口：一次请求拿到最近 N 篇的标题、时间、摘要和正文链接，不用逐页爬列表。

## 六、Meta 标签：从"几乎没有"到一整套

老站几乎没有这些标签，所以这次给每个页面补齐：

- 真实的 meta description、canonical URL、robots 标签，以及 `lang="en"`；
- Open Graph 与 Twitter card 标签，包括 `article:published_time` 和 `modified_time`；
- JSON-LD 结构化数据：每篇文章有 `BlogPosting` 和面包屑，索引页有 `WebSite`、`Blog`，以及代表他本人的 `Person`。

描述这一项比他预想的脏。旧数据库里存着每篇文章的"现成描述"，但它是把段落直接拼起来的（`…your app.The RevealWhat do I mean…`），还有几条里带着裸露的 HTML 标签。现在改成**从渲染后的正文里生成摘要**。

另外修了 2014 年留下的两处老 markup：

- viewport 标签里带着 `maximum-scale=1`，它会让手机用户没法双指放大——删掉；
- 完全没有 `og:image`，于是分享文章时 X 和 Threads 会抓页面上的第一张图，而那恰好是顶部那张 Little Bites of Cocoa 的横幅。现在每篇都有 1200×630 的预览卡。

## 七、IndexNow：发布就通知，30 行脚本

最后是一个 IndexNow key，加一个脚本：每次发文就 ping 一下 Bing 和它的合作方。Google 不参加这个协议，但 **Bing 的索引是很多 AI 搜索工具的数据来源**，所以这 30 行脚本值得写。

他对整件事的总结很克制：**没花多少时间，而且大部分只是文件。** 如果你也有一个博客，起点就是两样东西——`llms.txt`，以及每篇文章的 `.md` 版本。文章末尾他自己留了一条验证命令：

```shell
curl -H "Accept: text/markdown" https://deallocatedobjects.com/posts/making-my-blog-readable-by-ai-agents

```

## 八、我们的整理：三种读者、一份自查、五条边界

原文是"我做了什么"，下面是我们按它整理出来的"你的站该怎么判断"。

**三种读者要的东西不一样**

|       | 人（浏览器）       | 搜索爬虫                       | AI agent       |
| ----- | ------------ | -------------------------- | -------------- |
| 主要读什么 | 渲染后的页面、排版、交互 | HTML、canonical、结构化数据       | 正文纯文本，越长越好一次拿全 |
| 最怕什么  | 弹窗、跳转、慢、字太小  | 重复内容、缺 canonical、JS 才渲染的内容 | 模板噪音、分页、取不到正文  |
| 一次看多少 | 一篇           | 一批 URL                     | 通常一两页，但要能直接引用  |
| 给你的回报 | 停留、转化        | 排名、收录                      | 被复述、被引用、被当成来源  |

这张表解释了一个常见错配：很多"优化"是给人做的（加载速度、视觉），对 agent 完全不起作用；而对 agent 有用的东西（`.md`、`llms.txt`、干净的 meta、清楚的 canonical）**几乎不会伤到人**。所以两边不冲突，只是没人做。

**30 分钟自查：五条命令**

在你的站上依次跑，能定位到"哪一层缺了"：

```shell
# 1) 有没有 markdown 端点
curl -sI https://your.site/posts/some-post.md | head -1

# 2) 支不支持 Accept 协商（关键：看 Content-Type）
curl -s -o /dev/null -D - -H "Accept: text/markdown" https://your.site/posts/some-post | grep -i -E "content-type|vary|link|x-robots"

# 3) 有没有 llms.txt
curl -s https://your.site/llms.txt | head -20

# 4) robots.txt 里 agent 有没有被点名（以及有没有被拦）
curl -s https://your.site/robots.txt | grep -i -E "GPTBot|ClaudeBot|PerplexityBot|Google-Extended|Content-Signal"

# 5) 结构化数据在不在
curl -s https://your.site/posts/some-post | grep -o '"@type":"[A-Za-z]*"' | sort -u

```

判据很硬：第 2 条如果只回 `text/html`，说明协商没生效；`Vary` 缺失就不用往下做了，先修缓存那一层。

**三个判断，比配置更容易做错**

1. **canonical 与 `noindex` 必须成对出现。** 只发 `.md` 不声明 canonical，搜索引擎会把它当重复内容；只 `noindex` 不给 canonical 链接，agent 拿到了正文却不知道原始页面是哪个，引用就指不回来。两个头是一件事的两半。
2. **`Content-Signal` 是表态，不是协议。** 它不会强制任何人遵守，写 `ai-train=no` 也不等于模型方就会照做。它的真实作用是把"你的意图"写进可被机器读到的位置，将来出纠纷时有据可依。
3. **`llms.txt` 现在不保证被谁读，但成本极低。** 它没有标准地位，主流模型厂商也没承诺采纳。判断方法不是"有没有用"，而是"维护它要多少成本"——如果它和你的文章清单是同一份构建产物，成本接近零，那就是划算的。

**能不能抄到你的站：按平台能力粗分**

| 你的站                            | .md 版本  | Accept 协商 | llms.txt       | IndexNow |
| ------------------------------ | ------- | --------- | -------------- | -------- |
| 静态站 + 自建 nginx                 | 可做      | 可做        | 可做             | 可做       |
| 自托管 Ghost / WordPress（能改主题与反代） | 需写路由或插件 | 需反代层拦截    | 可做             | 可做       |
| 托管型博客平台（不能碰服务器）                | 多半不行    | 不行        | 看平台是否允许根目录静态文件 | 看平台      |
| 纯 SaaS 内容站                     | 不行      | 不行        | 不行             | 不行       |

最后一行不是嘲讽：平台不给这些能力的时候，能做的只有把内容写清楚、把标题和摘要写对——而这恰恰是权重最大的一项。

**站内几条同一条线**

- [AI 爬虫流量：11 个问答分清爬取、引荐与转化](https://www.hubwiz.com/blog/ai-bot-traffic-questions-answered/)——先分清"AI 在读你"和"有人从 AI 那里点进来"。
- [AI 爬虫冒充读者：近 5 倍流量暴涨的取证与 AWS WAF 拦截](https://www.hubwiz.com/blog/ai-bot-traffic-forensics-aws-waf/)——反过来的一面：不是所有自称 AI 的流量都值得放进来。
- [AI 读到的不是你看的那页：WhatsApp 隐藏指令事件](https://www.hubwiz.com/blog/whatsapp-hidden-ai-notes-ai-search/)——同一套"给机器读的文本"，被用在不想让人看见的地方时是什么样。

**五条边界，别把它读成"照做就能被引用"**

- **这是个人博客的经验，样本是 1。** 原文没有给出任何流量或引用数据，也没有"改完之后被引用次数上升"的证据。它证明的是"这些事可以做，且不难做"，不是"做了就有效"。
- **`llms.txt` 与 `Content-Signal` 都还没有标准地位。** 引用它们时要说明性质：一个是社区提案，一个是内容用途声明，都不是被广泛实现的协议。
- **Accept 协商依赖你能改服务器。** 托管型博客做不到，这不是技术难度问题，是权限问题。硬要绕（用 CDN 的 edge function）也能做，但维护成本会转移。
- **`.md` 端点相当于多开一个入口。** 凡是多开的入口都会被扫、被抓、被拿去训练；原文的做法（canonical + noindex + 明确的 robots 声明）是"开得可控"，不是"开得安全"。
- **别指望它替代基础 SEO。** 标题、摘要、内链、可抓取性这些老东西仍然是主干；这篇讲的是在主干已经通的情况下，多铺一条给 agent 的车道。

**来源**：Jake Marsh,《Making My Blog Readable By AI Agents》, Deallocated Objects, 2026-10-24。原文链接：[https://deallocatedobjects.com/posts/making-my-blog-readable-by-ai-agents](https://deallocatedobjects.com/posts/making-my-blog-readable-by-ai-agents?ref=hubwiz.com)。原文代码块中的 `map`/`rewrite` 配置与 `curl` 命令按原样保留；表 1（三类读者）、自查命令、平台能力表与"三个判断"为我们整理，非原文内容。

本文由汇智网编译整理，转载请注明出处。

> **这是「AI 基建 / 评测 / 安全」专题的一部分。**完整地图见：[AI 基建 / 评测 / 安全完全指南](https://www.hubwiz.com/blog/ai-infra-stack/)