为什么 AI 搜索读不到你的 SPA(GPTBot、ClaudeBot 与 JavaScript 难题)
为什么 ChatGPT 这类 AI 搜索读不到我的 JavaScript 网站?
GPTBot、ClaudeBot、PerplexityBot 这类 AI 爬虫基本不执行 JavaScript,所以纯客户端渲染的 SPA 在它们眼里就是一张空壳。本文讲清楚怎么两分钟自测、为什么服务端渲染或静态 HTML 是正解,以及页面能被读到之后还要做什么才换得来引用。
如果你的站点是纯客户端渲染的单页应用,AI 答案引擎大概率看到的是一张空白页。ChatGPT、Claude、Perplexity 背后的爬虫会抓取你的 HTML,但通常不会运行那段把内容拼出来的 JavaScript。没有 JavaScript,就没有正文,也就没有引用。正解是把真正的文字放进服务器返回的那份 HTML 里——通过服务端渲染或静态生成——在任何脚本运行之前。这篇文章讲清楚问题为什么会发生、怎么在自己站点上两分钟确认、到底怎么修,以及为什么”能被读到”是必要条件,但还不足以真正被引用。
一句话结论:AI 爬虫只抓 HTML,不渲染
普通浏览器做两件事:先下载 HTML,再运行 JavaScript 把页面填满。大多数 AI 检索爬虫只做到第一步就停了。Vercel 分析了主流 AI 爬虫实际产生的流量,发现 ChatGPT 和 Claude 的爬虫会抓 JavaScript 文件,却从不执行——ChatGPT 的 bot 在 11.5% 的请求里拉了 JS,Claude 是 23.84%,而两种情况下那段代码都只是被当成死文本读进去,没有运行。它们的行为更像 curl,而不像 Chrome。Google 自己的搜索索引器确实会在第二趟里跑 JS,但那是独立的另一套系统,和喂给实时 AI 答案的那些爬虫不是一回事;你也没法指望 Googlebot 的渲染去补 GPTBot 从不执行的那部分。
所以对一个纯 SPA 来说,爬虫看到的就是首屏 HTML 响应里的东西,而那通常几乎什么都没有。一个 Create React App 或默认的 Vite 构建产物,发出的 index.html 主体基本上就是一个空的 <div id="root"> 加几个 script 标签。你的标题、产品文案、价格表、FAQ,全是浏览器加载之后由爬虫从不执行的那段代码拼出来的。从模型那一侧看,这个页面没有任何值得引用的文字。你的内容不是排名差——文档里压根没有内容可供爬虫去排名。
这跟”渲染慢”或”渲染不稳”是两码事。爬虫不是在等你的应用启动、等不及才放弃,而是读完那个 HTTP 响应的字节就走了。如果那些字节里没有你的文字,那后面的一切——向量化、检索、引用——都无从恢复。
怎么两分钟自测
不需要任何专门工具就能确认。两个检查就能说明全部问题,而且它们之间会互相矛盾——这一点正值得搞明白。
第一,禁用 JavaScript 后重新加载。Chrome 里打开 DevTools,调出命令面板(Ctrl/Cmd+Shift+P),运行 “Disable JavaScript”,然后刷新。如果页面变空白、只剩一个转圈的加载图标、或塌成一个光秃秃的页眉,那张坏掉的页面就大致是 AI 爬虫拿到的东西。像 Playwright 这样的浏览器自动化工具可以把同一个检查脚本化——禁用 JavaScript 加载某个路由,断言某句已知的正文存在——这样你能把它接进 CI,等哪天有人把某个区块挪回客户端组件时及时抓到回归。
第二,看服务器实际发出的原始 HTML,而不是渲染后的 DOM。跑一句 curl -s https://yoursite.com/your-page,在输出里搜一句你确定在正文里的话:
curl -s https://yoursite.com/pricing | grep -o "起价 ¥349"
如果 grep 搜得到,爬虫就读得到。如果只翻到一堆 script 标签和一个空挂载点,说明你的正文只活在 JavaScript 里,AI 爬虫看不到。浏览器的”查看源代码”显示的是同一份东西——没渲染过的响应,跟 Elements 面板不一样——如果你不想用终端,用它也行。
这两个视图之间的差距,就是整个陷阱所在。Elements 面板里的 DOM 永远看着是完整的,因为你打开它的时候浏览器早就替你跑过 JS 了。要信”查看源代码”和 curl,别信审查元素面板,也别信页面对你自己呈现的样子——你开着 JavaScript,爬虫没有。
修法:在服务端渲染,或者直接发静态 HTML
修法就是把真正的正文放进服务器返回的那份 HTML 里,在任何 JavaScript 运行之前。有两条成熟的路子,它们的取舍跟一直以来一样。
服务端渲染(SSR)是在服务器上、或边缘节点上,为每次请求生成页面 HTML。Next.js App Router 默认就把 Server Component 渲染成 HTML——正文在响应体里,客户端只为可交互的部分下发 JavaScript。你可以用 OpenNext on Cloudflare 把它跑在边缘,它把 Next.js 构建产物适配到 Cloudflare Workers,让喂给用户的那次渲染同样喂给爬虫,而且就近发生在请求落地的地方。爬虫拿到的是一份内容完整的页面,你的用户在 hydration 之后照样得到一个可交互的应用。
静态生成则是在部署时把 HTML 构建一次,然后给所有人发同一份文件。Astro 默认零 JavaScript,特别适合内容型站点——营销页、文档、博客文章这种两次部署之间正文很少变的内容。产物是纯 HTML 文件,可以从 Cloudflare Pages 这样的 CDN 直接发,热路径上没有渲染步骤。本站走的就是这条路,所以你现在读的这些字就在首屏 HTML 响应里,不是脚本拼出来的。
你不必把所有东西重写一遍。诚实的范围问题是:哪些页面需要被引用?答案几乎总是那些公开页——营销页、文档、文章、对比页。只把这部分迁到 SSR 或静态 HTML,就覆盖了绝大部分价值。登录墙后面那个应用可以继续是客户端渲染的 SPA,反正 AI 爬虫本来就进不去、也不该进去,因为把私有数据挡在授权后面本来就是对的。一个常见且合理的架构就是:一个静态或服务端渲染的公开站点,加一个独立的 SPA 后台,各做各擅长的事。
迁完之后,再跑一遍 curl 检查。正文应该就明明白白躺在响应体里。顺手把页面保持得快一点——同一套边缘渲染既帮爬虫,也帮你的 Core Web Vitals;一个能快速返回 HTML 的服务器,爬虫也更可能把它完整抓下来。这些本来就是技术 SEO 一直在管的管道活,只是 AI 爬虫把跳过它的代价抬高了。
能被读到是必要条件,但不充分
把正文放进 HTML 只是让你进了门,并不等于能被引用。模型能读到页面之后,真正换来引用的,是它能干净抽取、能信任的内容:靠前的清晰结论、真实的具体数字,以及告诉引擎这个页面讲的是什么的结构化数据——而不是让它从一段段散文里去猜。
研究给出了具体数字——它指向的那套做法,我们在白帽 GEO 到底是什么里展开讲过。发表在 KDD 2024 的生成式引擎优化论文,在 10000 条查询上拿真实 AI 答案引擎测试了九种具体的内容改动,发现加入统计数字、直接引述、以及标注来源,能把页面在生成式答案里的可见度(按位置加权的指标)提升约 30–40%,其中引述和统计数字是单项里最强的几个。同一项研究还指出,这种提升并不均匀:原本在常规搜索排名里靠后的页面收益最大——作者把它理解为 GEO 在为体量较小的站点拉平场地,而不是给本就领先的内容再加倍。落到实操上就是:把力气花在你当前排在中游的页面,而不是那些已经领跑的页面。
然后,把页面对引擎也做成可读的结构化数据,而不只是对人可读的散文。用 schema.org 的类型给内容打标记——Article、FAQPage、Organization、Product,看哪个合适——并遵循 Google 的结构化数据指南,这样引擎能解析实体和关系,而不是靠猜。结构化数据是页面被识别成一个可引用、可标注来源的来源、而非一整面没区分度的文字墙的一部分。
提醒一句别走捷径,因为”能读到却引不到”这个落差很容易把人引向坏主意。跳过隐藏指令那套把戏,也别迷信 llms.txt。有一项分析扫了 37894 个被 AI 引用过的域名,发现带 llms.txt 的站平均被引 6.8 次,不带的是 6.7 次——这个差距的 p 值是 0.85,等于说没有任何可测的优势,而且被引最多的那些站大多根本不发这个文件。而且 Google 自己的垃圾内容政策现在明说了:规模化内容滥用、以及操纵生成式 AI 回答的企图都算违规,所以伪装内容、或往页面里塞提示词,结果是被过滤,而不是被引用。这背后还有个要分清的区别:被抓取、被索引,跟被引用不是一回事。模型可以收下你的页面,却始终不把它端出来——正是因此,在访问问题解决之后,真正出力的是可抽取性和可信度。
为什么现在就该做
AI 主导的发现正在从新鲜事变成默认入口:越来越多的 B2B 采购正经由会读网页、自己决定引用谁的 agent 完成,产品也越来越需要做到机器可读才能参与竞争。如果这些 agent 抓了你的原始 HTML,只找到一个空的 <div>,那不管你的产品多好,你都缺席了那个答案——而且你在分析后台里都看不到这件事表现为排名下滑,因为根本没有可丢的曝光。你只是没进候选集。
这个检查只花两分钟。禁用 JavaScript、curl 一下你的核心页面,看看正文还在不在。如果不在,服务端渲染或静态 HTML 就是答案,而且这是一次性投入——之后越来越多的采购决策经由那些跑不动你脚本的模型,它就一直在帮你赚回来。先把内容弄进 HTML;然后用具体数字、来源和结构去把那次引用挣回来。
参考资料
- The rise of the AI crawler — 流量分析显示 ChatGPT 与 Claude 的爬虫会抓 JavaScript 文件但不执行(Vercel,2024)。
- Playwright — 浏览器自动化框架,用于在 CI 里脚本化”禁用 JavaScript”的渲染检查。
- Next.js App Router — Server Component 默认渲染成 HTML,把正文放进响应体(Next.js 文档)。
- OpenNext on Cloudflare — 把 Next.js 构建产物适配到 Cloudflare Workers、跑在边缘的适配器(OpenNext 文档)。
- Cloudflare Workers — 文中所指的边缘运行时,就近为请求提供服务端渲染页面(Cloudflare 文档)。
- Astro — 默认零 JavaScript 的静态站点框架,适合内容型页面(Astro 文档)。
- Cloudflare Pages — 直接发静态 HTML 产物的 CDN,热路径上没有渲染步骤(Cloudflare 文档)。
- Core Web Vitals — Google 的页面体验指标,边缘渲染同样能改善(web.dev)。
- GEO: Generative Engine Optimization — 同行评审论文(KDD 2024),测得统计数字、引述、标注来源带来约 30–40% 的可见度提升,靠后页面收益最大(Aggarwal 等,2024)。
- schema.org — 结构化数据类型词汇表(Article、FAQPage、Organization、Product 等),用于给内容打标记。
- Google 结构化数据指南 — 用 schema.org 类型标记内容、让引擎解析实体而非靠猜的官方参考(Google Search Central)。
- The llms.txt Effect: 37,894 Domains Scanned — 发现
llms.txt对 AI 引用没有统计显著优势(p=0.85)(Trakkr Research,2025)。 - Spam Policies for Google Web Search — Google 关于规模化内容滥用与操纵生成式 AI 回答的政策(Google Search Central)。
FAQ
常见问题
GPTBot 这类 AI 爬虫会执行 JavaScript 吗?
不会。Vercel 的流量分析发现,ChatGPT 和 Claude 的爬虫会抓取 JavaScript 文件,却从不执行——那段代码只被当成死文本读进去。它们的行为像 curl,不像 Chrome,所以任何靠浏览器加载后拼出来的内容,对它们都是不可见的。
怎么确认 AI 爬虫读不读得到我的站点?
两个检查。先在 DevTools 里禁用 JavaScript 后刷新——页面变空白或塌成光秃秃的页眉,那就大致是 AI 爬虫拿到的东西。再用 curl 拉页面,在原始 HTML 里搜一句你确定在正文里的话;响应体里搜不到,爬虫就看不到。
为了 AI 搜索,我得把整个 SPA 重写吗?
不用。只把需要被引用的页面迁到服务端渲染或静态 HTML——营销页、文档、文章、对比页。登录墙后面的应用可以继续是客户端渲染的 SPA,反正爬虫本来就进不去、也不该进去。
能被 AI 爬虫读到,就够被引用了吗?
不够——能读到只是进了门,不等于进了答案。模型能读到页面之后,引用给的是它能干净抽取、能信任的内容——靠前的清晰结论、带来源的具体数字、结构化数据。KDD 2024 的 GEO 论文实测,加入统计数字、引述和标注来源能带来约 30–40% 的可见度提升。