你是不是也遇到过:页面明明写得挺好,外链也在慢慢攒,结果 Google 搜索结果里就是不显示正文?点开一看,标题有、meta 描述有,但正文区域一片空白,或者只有一行 loading 动画——别急着怀疑 SEO 插件或服务器,大概率是 Googlebot 在渲染环节“卡住了”。
Googlebot 不是浏览器,但它现在确实会执行 JavaScript;它也不是传统爬虫,可它又没浏览器那么耐心。搞懂它怎么“看”你的页面,比堆关键词和外链更早一步决定你能不能被搜到。
为什么 Googlebot 要渲染两次?第一次和第二次的区别在哪?
Googlebot 的抓取流程确实是两步:先抓,再渲染。
第一次只是“拿 HTML”。它快速请求你的页面,拿到原始 HTML 文件,提取 <a> 链接、<meta> 标签、结构化数据这些静态信息。这一步它不执行 JS,也不等图片、字体、API 返回。如果你的主体内容靠 document.getElementById('app').innerHTML = ... 这种方式塞进去,那它第一次看到的就是个空 <div id="app"></div>。
第二次才是“真干活”。Googlebot 启动一个轻量 Chromium 实例(版本基本同步 Chrome 稳定版),加载你的 JS,触发 fetch,等 CSSOM 构建完,等 React/Vue 把虚拟 DOM 渲染成真实 DOM,最后截取最终的 DOM 树——这才是它用来索引的内容。
举个真实例子:一位做家居电商的朋友,产品页全用 Vue + Axios 动态拉取详情。上线后三个月,Google Search Console 显示“已抓取但未索引”的页面占比超 80%。我们查了抓取日志,发现第一次请求时,服务器因未开启 gzip、接口响应慢,HTML 返回耗时 4.2 秒;而 Googlebot 对首次 HTML 响应的等待上限实际只有 3 秒左右,直接超时放弃。第二次渲染阶段,又因为 CDN 把 .js 文件缓存成了 Cache-Control: public, max-age=31536000,导致新版 JS 加载失败。调整服务器响应速度 + 给 JS 加上版本哈希后,收录状态明显改善。
简单说:第一次决定它“知不知道你有这个页面”,第二次决定它“到底看没看见你写了啥”。
哪些技术细节会让 Googlebot 渲染“卡住”?3 个最常见的坑
Googlebot 的 Chromium 内核虽新,但它不是来给你测兼容性的——它只关心“能不能在有限时间内拿到可用内容”。很多前端习惯,在它这儿就是雷区。
第一个坑:JS 执行超时。
Google 没公开具体时限,但大量实测表明,从开始执行 JS 到生成最终 DOM,窗口期大概在 5–7 秒之间。一旦超时,它就停手,把当前能拿到的 DOM 当作最终结果返回。
比如一个用了 Next.js 的博客首页,嵌了一个第三方评论组件,这个组件要加载 500KB 的 JS,并且依赖另一个未配置 CORS 的 API。Googlebot 渲染时卡在请求阶段,整页正文都丢了。后来我们把评论模块改成 IntersectionObserver 触发懒加载,只在用户滚动到对应位置才加载脚本——首页渲染成功率立刻回升,收录延迟也缩短了不少。
第二个坑:客户端路由没留出口。
单页应用用 history.pushState 切换 URL 很流畅,但 Googlebot 不会主动点“下一页”按钮,也不会模拟点击导航菜单。它只会按你 HTML 里明写的 <a href="/product/123"> 去抓链接。
如果你的 /product/123 页面没有静态 <a> 链接指向它,也没有 <link rel="canonical"> 明确声明主 URL,Googlebot 就永远只渲染 / 首页,其他路由全是 404 或白屏。
第三个坑:内容藏在滚动之后。
无限滚动、分页加载、折叠面板……这些交互体验很棒,但 Googlebot 不会滚鼠标、不会点“展开”,它只抓取初始视口内渲染完成的部分。
如果你的核心参数表、规格文档、用户评价全放在第三屏以下,那它们对 Googlebot 来说,等于不存在。
如何用“预渲染”让 Googlebot 一次就能拿到完整内容?
既然等它现场渲染风险高,不如提前把“成品 HTML”准备好,让它一打开就看到全部内容。
预渲染不是黑魔法,本质就是:在构建时(build time)或请求时(request time),用无头浏览器跑一遍你的 JS,把最终 DOM 快照存成静态 HTML,专门喂给爬虫。
主流做法有两种:
方案一:服务端渲染(SSR),比如 Next.js 或 Nuxt.js。
用户访问和 Googlebot 访问走同一套逻辑:请求进来,服务器执行 JS,拼出完整 HTML,再返回。好处是内容实时、语义清晰;难点是服务器压力大,得配好缓存策略,比如 Vercel/Netlify 的边缘缓存,或自己加 Redis 缓存首屏 HTML。
方案二:静态预渲染(Prerender),比如用 Rendertron 或自建 Puppeteer 服务。
你在 Nginx/Apache 或 CDN 层加个判断:如果 User-Agent 包含 Googlebot 或 bingbot,就把请求转发给预渲染服务;它用无头 Chrome 渲染完,把 HTML 缓存下来,下次同类请求直接返回。不用改业务代码,部署一个 Docker 容器就能跑起来。
我帮一个用 Angular 做的房产平台做过这个改造。他们所有房源详情页都靠 API 异步填充,Googlebot 渲染失败率长期在 70% 以上。接入 Rendertron 后,失败率压到 5% 以内,关键是整个过程没动一行业务逻辑,只改了 Nginx 配置和加了个轻量服务。
怎么检测自己的页面是否被 Googlebot 正确渲染?3 个免费工具
别靠猜,直接看它“亲眼所见”的样子。
第一个:Google Search Console 的“网址检查”工具。
进 GSC → 左侧选“网址检查” → 输入你要查的页面 URL → 点“测试实时网址”。几秒后你会看到两个截图:“已抓取”(原始 HTML)和“已呈现”(渲染后 DOM)。如果“已呈现”里缺标题、没正文、价格栏是空的——说明渲染环节断了。建议每周抽 3–5 个核心落地页跑一遍,尤其发版前后。
第二个:Chrome DevTools 的“Coverage”功能。
打开页面 → F12 → Ctrl+Shift+P(Win)或 Cmd+Shift+P(Mac)→ 输入 Show Coverage → 回车 → 刷新页面。它会标出每个 JS 文件有多少代码真正被执行。如果某个关键初始化脚本显示“0% used”,那 Googlebot 极大概率也没执行它——可能是被 async/defer 拖太晚,也可能是被条件判断直接跳过了。
第三个:Google 的 Mobile-Friendly Test。
别被名字骗了,它底层调用的就是 Googlebot 渲染引擎。输入 URL 后,它会展示渲染快照,并列出所有加载失败的资源(比如 404 的字体、被跨域拦截的 API、超时的 JS)。上次我帮一个企业站排查,就是靠这个工具发现一个 fonts.googleapis.com 的 CSS 请求被国内网络阻断,导致整个页面样式失效、JS 渲染卡死。
哪些 JS 特性是 Googlebot 能处理的,哪些是它不支持的?
Googlebot 用的是 Chromium,所以大部分现代语法它都认:const/let、async/await、Promise、fetch()、IntersectionObserver(注意:只观察初始视口)、甚至 Web Workers(只要主线程能拿到结果)。
但它明确不模拟用户行为:
- 不触发
click、scroll、mouseover这类事件 - 不写入
localStorage/sessionStorage(读可以,写会被忽略) - 不使用 Service Worker 缓存(即使注册成功,它也不会走 SW 的 fetch 逻辑)
- 忽略
document.write()(已废弃,它直接跳过) - 不等
requestAnimationFrame动画结束(动画中插入的节点,可能根本来不及出现在最终 DOM 里)
踩过一个典型坑:某 SaaS 后台首页用 Service Worker 缓存了 HTML,但没加 Cache-Control: no-cache 响应头。结果 Googlebot 每次都拿到缓存的旧 HTML,连新上线的功能入口都看不到。加了头部强制绕过 SW 后,问题当天解决。
结尾:今天就能做的 1 个操作
打开你正在用的 Google Search Console → 左侧点“网址检查” → 粘贴你最近一周流量最高的那个页面 URL → 点“测试实时网址”。
等结果出来后,直接对比“已抓取”和“已呈现”两个标签页:
- 如果“已呈现”里标题、H1、核心段落、价格或 CTA 按钮不见了,
- 那就打开 Chrome DevTools 的 Network 面板,重新加载一次页面,
- 找出耗时超过 3 秒的 JS 或返回 4xx/5xx 的请求,
- 把它改成
async加载,或用loading="lazy"控制时机, - 如果是第三方脚本(比如统计、客服、广告),先注释掉它跑一次测试。
整个过程不到 10 分钟。做完,你就知道 Googlebot 看到的,到底是你的精心设计,还是一张白纸。