你的页面对 Google 来说不存在 —— 一次微前端 SEO 链路排查

85 阅读11分钟

本文用 Yukiss(一个虚构的手帐文具独立站)来还原一次真实的排查过程。URL 和场景做了替换,技术问题是真实的。

前言

最近在做一个微前端项目的 SEO 排查,第一步打开 Google Search Console,看到的是这个:

URL is not on Google。

不是排名低,不是收录延迟——是这个页面,Google 完全不知道它存在。

我的第一反应是渲染问题。微前端内容依赖客户端 JS 异步加载,Googlebot 应该看不到——这个猜测太合理了,合理到我已经开始盘算 SSR 改造方案了。

但打开 GSC 准备看渲染结果的那一刻,我意识到假设方向就是错的。

这篇文章记录了那次排查的完整过程,以及从中整理出的一个四层诊断框架。如果你也遇到过"页面明明能访问,但 Google 搜不到"这个问题,这个框架可能对你有用。


Google 收录一个页面,是四层顺序执行的链路

在开始排查之前,先把认知基础对齐。

Google 把一个页面收录到搜索结果里,不是一个动作,而是四层顺序依赖的链路:

第一层:链接发现
Google 能知道这个 URL 存在吗?
        ↓ 通过才进入下一层
第二层:可访问性
Google 能成功拿到这个页面吗?
        ↓ 通过才进入下一层
第三层:内容渲染
Google 能看到页面里的内容吗?
        ↓ 通过才进入下一层
第四层:语义理解
Google 知道这个页面在说什么吗?

第一层断了,后面三层根本没有机会执行。

大多数讨论微前端 SEO 的文章,都集中在第三层——渲染延迟、SSR 改造、两波索引机制。这不是没有原因的:渲染问题有大量实验数据支撑(Onely 的实验显示 Googlebot 跟踪 JS 链接比 HTML 链接慢 9 倍),有 Google 官方文档背书,讨论起来有料可说。

但渲染是链路的中间,不是起点。如果从第三层开始排查,很容易在错误的层上消耗精力。


三个断点:从上游到下游逐层排查

第一层:Google 能发现这个 URL 吗?

先找商品详情页的入口在哪里。

Yukiss 的商品目录板块 URL:

https://yukiss.com/shop#products

#products 后面是微前端注入的商品卡片列表,用户能正常看到,点击可以跳转到详情页。看起来一切正常。

# 之后的内容,Google 会直接忽略。

这不是 Google 的特殊处理,而是 URL 规范本身的定义:# 后面的 fragment identifier 只在浏览器本地处理,不会发送给服务器,爬虫遵循同样的规则。Google 在 2015 年就正式废弃了曾经支持 hash-based 导航的 hashbang(#!)方案。

所以 Google 实际抓取的是:

https://yukiss.com/shop

商品卡片列表——不存在。详情页的链接——从来没有被发现过。

快速自查:

# 检查入口链接的形式
# 可爬取:标准 <a href> 标签
<a href="/products/ts-B6-grid-2024">查看商品</a>

# 不可爬取:JS click handler
<div onclick="navigate('/products/ts-B6-grid-2024')">查看商品</div>

# 不可爬取:链接藏在 #hash 后面
https://yukiss.com/shop#products  → Google 只看到 https://yukiss.com/shop

除了直接检查代码,还可以用 Screaming Frog 爬取站内链接,看爬虫视角下能发现哪些 URL。

第一层,断了。


第二层:Google 能成功访问这个 URL 吗?

假设 sitemap 里收录了详情页的完整 URL:

https://yukiss.com/product-detail?id=ts-B6-grid-2024&type=notebook

尝试去掉参数,直接访问 shell 页面:

curl -o /dev/null -s -w "%{http_code}" https://yukiss.com/product-detail
# 返回:404

404。

这个结果一开始让我有点困惑——用户访问的时候明明是正常的。

后来想明白了:/product-detail 这个路由,在服务端根本不存在。它是微前端在客户端动态处理的路径,服务端没有对应的逻辑。有 ?id= 参数,客户端 JS 能发请求、渲染内容;没有参数,服务端直接返回 404,客户端 JS 根本没有机会运行。

Google 看到 404,会直接跳过,不进行索引。Google 官方文档明确说明:非 200 状态码的页面,渲染可能直接被跳过。

换句话说:即使 sitemap 里收录了带参数的完整 URL,只要 Google 在任何环节拿到了不带参数的 shell URL,就会直接碰壁。

快速自查:

# 去掉所有参数,直接访问 shell URL
curl -o /dev/null -s -w "%{http_code}" https://your-domain.com/page-path
返回结果含义
200,有实质内容这层没问题
404服务端不认识这个路由,依赖客户端 JS 动态处理
200,但内容是错误页或空白软 404,Google 可能判定为无效页面不予索引
需要登录Google 无法通过验证,内容不可见

这个断点在日常开发中很难被感知到——因为开发者总是带着参数访问页面,从来不会直接访问 shell URL。

第二层,也断了。


第三层:Google 能看到内容吗?

到这里,"URL is not on Google"已经有了足够的解释。但继续往下看,第三层同样有问题。

操作步骤:

  1. 打开 Google Search Console
  2. 左侧导航 → 网址检查(URL Inspection)
  3. 输入要检查的完整 URL(带参数)
  4. 点击 "查看已抓取的网页(View Crawled Page)"
  5. 切换到 HTML tab
  6. Ctrl+F 搜索页面核心关键词(商品名、标题等)

结果:什么都没有。

商品标题、描述、价格,全部由 JS 在客户端异步拉取。Googlebot 第一波抓到的,是一个几乎空白的 HTML shell:

<!DOCTYPE html>
<html>
  <head>
    <title>Yukiss</title>  <!-- 所有详情页共用同一个 title -->
  </head>
  <body>
    <div id="app"></div>
    <script src="/remoteEntry.js"></script>  <!-- 微前端入口 -->
  </body>
</html>

这和两波索引机制直接相关:Google 第一波拿到空壳,判断需要渲染,放进渲染队列。但渲染队列不是即时的。Onely 的实验数据显示,某些页面的渲染延迟长达数周。即使进入第二波渲染,内容能否完整取回,还取决于 JS 执行成功率、API 响应速度、Google 给该页面分配的渲染资源。

快速自查:

HTML tab 搜索结果含义
能找到核心关键词内容在渲染结果里,这层没问题
找不到内容依赖 JS 异步加载,Googlebot 第一波没有等到
截图是空白或 loading 状态JS 执行失败,或关键资源被 robots.txt 拦截

值得注意的是:Google 在 2026 年 3 月更新了 JavaScript SEO 官方文档,移除了之前"JS 对 SEO 不友好"的笼统警告,表明 Googlebot 渲染能力已经成熟。这不是说渲染问题消失了,而是说问题已经从"Google 能不能渲染 JS"转移到了"渲染链路完不完整"——前两层如果没问题,渲染层本身的障碍已经小了很多。

第三层,同样断了。

三层全部断,而且是从最上游开始断的:

断点一:#hash 截断 → Google 从未发现详情页链接
    ↓
断点二:去掉参数 → 404 → URL 无法独立存在
    ↓
断点三:纯 CSR → 初始 HTML 是空壳
    ↓
结果:URL is not on Google

四层诊断框架:可复用的排查顺序

把这次排查过程整理成可复用的形式。这不是解法清单,而是一个排查顺序——先找到断在哪一层,再决定从哪里下手。


第一层:链接发现

核心问题: Google 是通过什么路径知道这个 URL 存在的?

诊断工具:

  • GSC → 链接报告,查外部链接和内部链接来源
  • Screaming Frog 爬取站内链接,模拟爬虫视角
  • 检查 sitemap.xml 是否收录了完整 URL

常见断点:

现象原因
入口链接在 #hash 后面fragment 不传给服务器,Google 看不到
链接用 JS click handler 生成没有 href 属性,不被识别为可爬取链接
sitemap 只收录了 shell URL带参数的详情页 URL 从未被主动提交
分页内容通过 JS 加载第二页、第三页的链接 Google 可能发现不了

第二层:可访问性

核心问题: 不带任何参数直接访问这个 URL,返回什么?

诊断工具:

# 检查 shell URL 状态码
curl -o /dev/null -s -w "%{http_code}" https://your-domain.com/page-path

# 检查 robots.txt 是否拦截了关键路径
curl https://your-domain.com/robots.txt

常见断点:

返回结果含义处理方向
404服务端不认识这个路由配置 catch-all,返回 200 + 骨架 HTML
软 404(200 但内容是错误页)Google 可能判定为无效页面确保 shell 返回有意义的内容
被 robots.txt 拦截关键资源或路径被误封检查 Disallow 规则

第三层:内容渲染

核心问题: Googlebot 渲染完页面后,HTML 里有没有实际内容?

诊断工具:

GSC → 网址检查 → 查看已抓取的网页 → HTML tab → 搜索核心关键词

也可以用 Google 的 Rich Results Test(适用于公开可访问的页面):

https://search.google.com/test/rich-results

常见断点:

现象原因处理方向
搜不到核心关键词内容由 JS 异步拉取,第一波没等到考虑 SSR 或服务端预渲染关键字段
截图是空白JS 执行失败检查 JS 报错、资源加载情况
title 所有页面一样title 由 JS 渲染,Google 只拿到了默认值服务端输出动态 title

第四层:语义理解

核心问题: Google 知道这个页面在说什么吗?

诊断工具:

# Rich Results Test
https://search.google.com/test/rich-results

# Schema Markup Validator
https://validator.schema.org/

常见断点:

现象原因处理方向
所有详情页 title 相同JS 渲染的 title,Google 只读到了 shell 默认值服务端输出 <title>
没有结构化数据缺少 JSON-LD添加 Product / Course 类型的 JSON-LD
canonical 指向 shell URLJS 动态设置 canonical,Google 可能只读到初始值服务端输出正确的 canonical

这一层是四层里修复成本最低的:不涉及架构改动,只需要改 HTML 模板或加 JSON-LD 脚本。即使前三层暂时改不动,第四层也可以立刻做。

一个最小可行的改动:

<!-- 服务端根据 URL 参数注入,不依赖 JS -->
<head>
  <title>Indigo A5 Grid Notebook 2024 - Yukiss</title>
  <meta name="description" content="手工装订,80g 道林纸,适合日常手帐记录。">
  <link rel="canonical" href="https://yukiss.com/product-detail?id=ts-B6-grid-2024&type=notebook">
  <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "Product",
    "name": "Indigo A5 Grid Notebook 2024",
    "description": "手工装订,80g 道林纸,适合日常手帐记录。",
    "url": "https://yukiss.com/product-detail?id=ts-B6-grid-2024&type=notebook"
  }
  </script>
</head>

找到断点之后

诊断清楚了,但修不修得动是另一回事。

不同层的修复成本反向于链路顺序:

第四层(语义)  → 改 HTML 模板、加 JSON-LD,成本最低,可以立刻做
第三层(渲染)  → 引入 SSR 或边缘预渲染,工程量较大
第二层(可访问)→ 改服务端路由配置,需要跨团队协作
第一层(发现)  → 改路由架构或 CMS 结构,成本最高

一个实用的思路:从最下游开始修,能改的先改。 哪怕上游的断点暂时动不了,至少把下游的层做干净。

另外有一件值得单独说的事:微前端架构里,这四层往往分属不同团队——路由在 CMS 侧,渲染在前端团队,服务端配置在运维,meta 和结构化数据在内容团队。每个团队都在做自己那一层,但没有人在 track 整条 SEO 链路的健康状态

这不是哪个团队的问题,而是一个值得提前想清楚的问题:这个页面的 Google 可见性,应该由谁来 track,用什么指标衡量,出了问题谁先知道?

如果这个问题没有答案,断点可能存在很久,才会有人注意到。


总结

遇到"URL is not on Google",先不要假设断点在渲染层。按这个顺序从上游开始查:

能发现吗 → 能访问吗 → 能看到内容吗 → 能理解语义吗

找到断在哪一层,再决定从哪里下手。

这个排查顺序是从这次经历里提炼的,理论上适用于更广的场景,但我还没有在其他项目上完整跑过——如果你用这个框架查出了不一样的断点,欢迎在评论区交流。


参考资料