如果你的独立站用了 Next.js、Nuxt 或其他 Headless 框架,Google Search Console 里却持续出现「已抓取但未建立索引」「URL 未收录」,或者你已经部署了结构化数据却在 Rich Results Test 看不到,这篇文章针对的就是这些问题。
相关问题可以参考 Shopify 技术 SEO 检查清单 和 跨境独立站技术 SEO 审计。
Headless 架构和传统 CMS 的 SEO 差异
传统 WordPress 或 Shopify(Liquid 模板)的页面,服务器直接输出完整 HTML,Googlebot 拿到的内容和用户浏览器看到的基本一致。Headless 架构把内容层(CMS/API)和展示层(前端框架)分开,页面内容通常由 JavaScript 在客户端或服务端动态组装。
这带来三个 SEO 层面的关键区别:
- 渲染时机不同:内容什么时候生成 HTML,直接决定 Googlebot 能不能拿到文本
- JS 依赖更重:Googlebot 爬取队列中,HTML 解析先于 JS 执行,客户端渲染的内容有延迟被索引的风险
- 内容与 URL 分离:canonical、hreflang、sitemap 需要在代码层主动维护,不像传统 CMS 有现成插件自动处理
以下 5 个坑是 Headless 项目里复现频率最高的问题。
坑一:CSR 页面 Google 抓不到动态内容
三种渲染方式的区别
| 渲染方式 | 全称 | HTML 生成时机 | Googlebot 可见性 |
|---|---|---|---|
| SSG | Static Site Generation | 构建时 | ✅ 立即可见 |
| SSR | Server-Side Rendering | 请求时服务端 | ✅ 立即可见 |
| CSR | Client-Side Rendering | 浏览器端 JS 执行后 | ⚠️ 延迟或无法收录 |
CSR 是最容易出问题的模式。如果你的产品列表页、分类页用 CSR 从 API 拉数据渲染,Googlebot 第一次抓取时看到的可能是空壳 HTML,关键内容不在里面。
如何判断你的页面用了哪种渲染方式
方法一:用 curl 模拟请求
curl -A "Googlebot/2.1 (+http://www.google.com/bot.html)" https://yoursite.com/products/category-name
如果返回的 HTML 里没有产品名称、描述等核心文字,只有 <div id="__next"></div> 这类容器,说明内容在客户端渲染。
方法二:在浏览器禁用 JavaScript 查看页面
Chrome → 开发者工具 → Settings → Debugger → Disable JavaScript,然后刷新页面。如果页面变空,内容依赖 JS 渲染。
方法三:Google Search Console URL 检查工具
粘贴 URL → 「测试实际 URL」→ 查看「已渲染的页面」截图,以及「页面资源」是否有加载失败。
修复方向
- 产品详情页、分类页、博客页优先改为 SSG(
getStaticProps/generateStaticParams) - 需要实时数据的页面(价格、库存)考虑 SSR 或 ISR(增量静态再生)
- 避免把 SEO 关键内容放在
useEffect里加载
坑二:Next.js Hydration 问题导致结构化数据不输出
常见现象
在代码里写了 <script type="application/ld+json"> 的 JSON-LD,但在 Google Rich Results Test 里测试时「找不到结构化数据」,或显示「找到但有错误」。
根本原因
Next.js 的 Hydration 指服务端渲染的 HTML 在客户端被 React 接管的过程。如果服务端和客户端渲染的 DOM 不一致(Hydration Mismatch),React 会重新渲染并覆盖 DOM,导致服务端注入的 JSON-LD 被清空。
常见触发场景:
- 结构化数据内容包含时间戳或随机值,服务端和客户端生成的值不同
- JSON-LD 的数据来源于
window对象或浏览器 API(仅客户端可用) - 使用了某些第三方组件,内部有
suppressHydrationWarning掩盖了不一致
排查方法
- 打开 Chrome DevTools Console,搜索
Warning: Prop或Hydration failed - 用
view-source:前缀直接查看服务端输出的 HTML 源码(不是 DevTools 里的动态 DOM)
view-source:https://yoursite.com/products/product-name
在源码里搜索 application/ld+json,确认 JSON-LD 是否在服务端就输出了。
- 用 Google Rich Results Test 的「代码」标签查看测试时拿到的 HTML
修复方向
在 Next.js 里,JSON-LD 应当在服务端组件(Server Component)或 generateMetadata 里生成,避免在客户端组件里动态构建:
// app/products/[slug]/page.tsx
export default function ProductPage({ product }) {
const jsonLd = {
"@context": "https://schema.org",
"@type": "Product",
name: product.name,
description: product.description,
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* 页面内容 */}
</>
);
}
坑三:canonical 和 hreflang 在 API-first 架构里的处理
问题场景
Headless 商城通常有多个 URL 可以访问同一内容:
yoursite.com/products/red-dressyoursite.com/collections/dresses/red-dressyoursite.com/?product_id=1234
如果没有正确设置 canonical,Google 会自己选择「规范 URL」,可能不是你想要的。hreflang(多语言站)如果在代码里漏掉,多语言版本互相争排名。
canonical 动态生成的正确方式
在 Next.js App Router 里,用 generateMetadata 统一处理:
// app/products/[slug]/page.tsx
export async function generateMetadata({ params }) {
return {
alternates: {
canonical: `https://yoursite.com/products/${params.slug}`,
languages: {
"en-US": `https://yoursite.com/en/products/${params.slug}`,
"zh-CN": `https://yoursite.com/zh/products/${params.slug}`,
},
},
};
}
避免的做法:
- 不要在
useEffect里用 JavaScript 动态修改<link rel="canonical">的href,Googlebot 执行 JS 前已经记录了原始值 - 不要让 CMS 和前端框架各自输出一个 canonical,产生冲突
hreflang 常见错误
- 双向引用缺失:A 语言指向 B 语言,B 语言也必须反向指向 A
- x-default 缺失:需要有一个
hreflang="x-default"指向语言选择页或默认语言版本 - URL 与实际可访问地址不一致:hreflang 里写的 URL 必须返回 200 状态码
坑四:SPA 路由切换导致内链和面包屑失效
问题描述
Headless 站点通常用 SPA(单页应用)路由,页面切换通过 JavaScript 更新 DOM,不触发完整的 HTTP 请求。对用户体验好,但对 Googlebot 的链接跟踪有潜在影响。
Googlebot 依赖 <a href="..."> 标签发现新 URL。如果你的内链通过 JavaScript 事件(onClick)触发路由而没有 href 属性,Googlebot 可能无法发现这些链接。
验证方法
在 GSC 的「链接」报告里检查内链数量是否合理。也可以用 Screaming Frog 或 Ahrefs Site Audit 爬取站点,看到的内链数和你预期是否一致。
修复方向
Next.js 的 <Link> 组件默认会渲染为带 href 属性的 <a> 标签,这是安全的。需要注意的是:
- 面包屑导航必须用真实的
<a href>链接,不要用纯 JS 点击事件替代 - 分页链接(第 2 页、第 3 页)必须有可爬取的 URL,不能只是 JS 状态
- 筛选器(颜色、尺寸)如果影响 SEO 目标页面,需要有对应的可爬取 URL
坑五:sitemap 动态生成的实时性和格式问题
两类常见错误
错误一:sitemap 是构建时生成的静态文件,新内容上线后没有更新
如果你的 sitemap 在 next build 时生成并缓存,新发布的产品页或博客页不会自动加入。Google 依赖 sitemap 发现新内容,这会直接影响新内容的收录速度。
解决方案:使用 Next.js 的动态 sitemap 路由:
// app/sitemap.ts
import { getAllProducts, getAllBlogs } from "@/lib/api";
export default async function sitemap() {
const products = await getAllProducts();
const blogs = await getAllBlogs();
return [
{
url: "https://yoursite.com",
lastModified: new Date(),
changeFrequency: "daily",
priority: 1,
},
...products.map((p) => ({
url: `https://yoursite.com/products/${p.slug}`,
lastModified: new Date(p.updatedAt),
changeFrequency: "weekly",
priority: 0.8,
})),
...blogs.map((b) => ({
url: `https://yoursite.com/blog/${b.slug}`,
lastModified: new Date(b.updatedAt),
changeFrequency: "monthly",
priority: 0.6,
})),
];
}
错误二:sitemap 包含 noindex 页面或返回非 200 状态码的 URL
sitemap 里列出的 URL 应当全部可正常访问。定期检查 GSC「站点地图」报告,查看「已发现但未建立索引」的数量。
如何系统验证 Headless 站点的 SEO 状态
按以下顺序排查,能覆盖大部分 Headless SEO 问题:
第一步:GSC URL 检查工具
选取有代表性的页面(首页、产品页、分类页、博客页),逐一用「URL 检查 → 测试实际 URL」检查:
- 已渲染页面截图是否正常
- 索引状态和上次抓取时间
- 是否有资源加载失败
第二步:查看 Google 缓存版本
在 Google 搜索框输入 cache:yoursite.com/products/slug,对比缓存版本和实际页面的内容差异。缓存内容缺失说明 Googlebot 抓取时 JS 未完全执行。
第三步:Google Rich Results Test
对有结构化数据的页面(产品页、文章页、面包屑)逐一测试,确认数据是否被正确识别。
第四步:view-source 检查关键内容
用 view-source: 查看服务端实际输出,重点看:
<title>和<meta description>是否输出<link rel="canonical">值是否正确- JSON-LD 是否在 HTML 里
[2026 技术实战提示] 在真实的商业环境中执行上述策略时,请始终以官方最新文档的 API 参数或界面变动为准。建议配合 GTM Preview 和 Google Search Console 进行实时验证。
FAQ
Next.js 做独立站 SEO 比 Shopify 好吗?
这个问题没有绝对答案,取决于你有没有技术团队维护。Shopify 内置了大量 SEO 基础设施(canonical、sitemap、结构化数据),开箱即用,出错概率低。Next.js 灵活性更高,但所有 SEO 相关配置都需要自己实现和维护——你要自己处理 canonical、robots.txt、sitemap、元标签、结构化数据。如果没有专人维护,Next.js 反而更容易出 SEO 问题。
用了 CDN 还需要 SSR 吗?
CDN(包括 Cloudflare)缓存的是服务器已生成的 HTML。如果你的页面本身用 CSR,CDN 缓存的就是空壳 HTML,对 SEO 没有改善。SSR/SSG 和 CDN 解决的是不同问题:前者决定内容什么时候生成,后者决定内容从哪里分发。需要 SEO 的页面,CDN 无法替代 SSR/SSG。
Headless 商城用 CSR 会被 Google 惩罚吗?
Google 官方表示不会直接惩罚 CSR 页面,但 CSR 页面的收录速度更慢,内容可见性更低。实际影响是:产品页可能长期处于「已抓取但未建立索引」状态,自然流量减少,不是排名下降,而是根本没有排名机会。这和惩罚的效果类似,但原因不同。
下一步:独立站架构 SEO 诊断
如果你的 Headless 站点在 GSC 里有以下任意一种情况:
- 「已发现但未建立索引」超过总 URL 的 20%
- 结构化数据报告里有大量错误
- 近 3 个月自然流量持续下降但内容没有减少
可以提交以下信息做技术诊断:
- 你使用的框架和版本(Next.js 13/14/15?App Router 还是 Pages Router?)
- GSC 索引报告截图(覆盖状态分布)
- 一个「已抓取但未建立索引」的典型页面 URL
技术诊断会给出具体的渲染方式判断、canonical 检查结果和优先修复清单。详细技术审计流程见 跨境独立站技术 SEO 审计。更多 SEO 相关文章见 SEO 专栏。
评论
留言需人工审核后才会显示;回复会随主评论一起发布。评论按文章独立归档,请在你阅读的那篇文章下留言。 技术诊断请发邮件 sue@sufob.com或查看联系说明。