Next.js SEO
一、先建立总模型
Next.js SEO 本质上要解决 5 件事:
可发现:搜索引擎能通过链接、sitemap、外链找到页面。
可抓取:robots、状态码、重定向、鉴权不会挡住重要页面。
可索引:页面有稳定 URL、canonical、可见正文、非重复内容。
可理解:标题、描述、语义 HTML、结构化数据、内链关系清晰。
可体验:首屏快、布局稳定、移动端好用、JS 不拖垮渲染。
一句话理解:
SEO 不是只写
title和description,而是保证搜索引擎能发现正确页面、抓到完整内容、判断它值得索引,并理解它应该展示给哪些搜索用户。
整体链路:
flowchart LR
A["发现 URL"] --> B["抓取页面"]
B --> C["渲染内容"]
C --> D["判断索引"]
D --> E["理解语义"]
E --> F["排名与展示"]
F --> G["GSC 监控"]
H["sitemap / 内链 / 外链"] --> A
I["robots / 状态码 / 鉴权"] --> B
J["SSG / ISR / SSR / RSC"] --> C
K["canonical / noindex"] --> D
L["metadata / 语义 HTML / JSON-LD"] --> E
M["Core Web Vitals / 移动端"] --> F几个概念的边界:
概念 | 主要作用 | 容易误解的点 |
|---|---|---|
| 帮搜索引擎发现重要 URL | 提交 sitemap 不等于保证收录 |
| 控制爬虫能访问哪些路径 | 不是禁止索引的首选方式,也不是安全机制 |
| 明确告诉搜索引擎不要索引页面 | 页面需要能被抓取,爬虫才能看到 noindex |
| 声明重复页面中的规范 URL | 只是信号,不会让用户跳转 |
| URL 真实迁移 | 旧 URL 永久迁移时比 canonical 更直接 |
| 声明标题、描述、分享、索引等页面信号 | 不能替代真实正文内容 |
| 用结构化数据描述页面实体 | 必须和页面可见内容一致 |
二、搜索引擎工作流与 GSC
搜索引擎工作流
Google 处理页面可以按这 6 步理解:
Discovery 发现:通过 sitemap、站内链接、外链、历史抓取记录发现 URL。
Crawling 抓取:Googlebot 请求页面 HTML、CSS、JS、图片等资源。
Rendering 渲染:对 JavaScript 页面,Google 会尝试渲染页面,但重要内容最好服务端输出。
Indexing 索引:Google 判断页面是否能进索引,会看
noindex、canonical、重复内容、内容质量等。Ranking 排名:索引后,页面根据搜索意图、内容质量、链接、页面体验等参与排序。
Serving 展示:Google 生成搜索结果里的标题、摘要、富结果、面包屑、图片等。
面试表达:
我理解 SEO 不是页面上线之后等排名,而是要保证发现、抓取、渲染、索引、理解和展示每一步都没有断点。Next.js 的价值主要在于可以通过 SSG、ISR、SSR 和 Server Components,让搜索引擎更早拿到完整 HTML,同时用 Metadata API、sitemap、robots、canonical 和 JSON-LD 补齐页面信号。
GSC 看什么
Google Search Console 是观察 Google 如何处理站点的工具,不是提高排名的按钮。
GSC 模块 | 用来看什么 | 对应阶段 |
|---|---|---|
Sitemaps | sitemap 是否被读取,提交 URL 数量是否合理 | 发现 |
URL Inspection | 单个 URL 是否可抓取、可索引,canonical 是否正确 | 抓取 / 索引 |
Pages | 全站 URL 的索引状态和未收录原因 | 索引 |
Performance | 点击、曝光、CTR、平均排名、query、page | 展示 |
Core Web Vitals | LCP、INP、CLS 是否有真实用户体验问题 | 体验 |
Enhancements | 结构化数据和富结果问题 | 理解 / 展示 |
URL Inspection 排查顺序
检查一个页面是否能被 Google 正常处理时,优先看这些点:
URL 是否已经在 Google 索引中。
Test Live URL当前是否可抓取。Page fetch是否成功,状态码是否是 200。Crawling allowed是否被 robots.txt 阻止。Indexing allowed是否被 noindex 阻止。User-declared canonical是否是预期 URL。Google-selected canonical是否和声明一致。Rendered HTML / screenshot 里是否能看到核心内容。
结构化数据是否被识别。
修复后再
Request Indexing。
常见 GSC 状态:
状态 | 含义 | 排查重点 |
|---|---|---|
Discovered - currently not indexed | Google 知道 URL,但还没索引 | 内链、sitemap、站点权重、内容质量 |
Crawled - currently not indexed | Google 抓过但没收录 | 薄内容、重复内容、页面价值 |
Duplicate, Google chose different canonical | Google 选择了别的规范页 | canonical、站内链接、sitemap 是否一致 |
Alternate page with proper canonical tag | 页面正确 canonical 到别处 | 如果是重复页,通常正常 |
Excluded by noindex tag | 页面被 noindex 排除 | 判断是否符合预期 |
Blocked by robots.txt | robots 阻止抓取 | 检查 |
Page with redirect | 这是重定向 URL | sitemap 不应该放重定向 URL |
Not found 404 | 页面不存在 | 修复链接、重定向或确认删除 |
三、提高 SEO 的核心方式
1. 让重要页面可发现
重要页面要能被搜索引擎找到。Next.js 中常见做法:
重要页面使用真实链接,比如
<Link href="/blog/a">,不要只靠 button +router.push()。用
app/sitemap.ts生成 sitemap,并且只放希望被索引的规范 URL。做好内链结构,比如导航、分类、面包屑、相关文章、推荐商品。
URL 尽量稳定,slug 改动后要做永久重定向。
2. 让重要页面可抓取
页面被发现之后,还要能被抓取。
公开 SEO 页面不要依赖登录态。
正常页面返回 200,不存在返回 404 / 410,永久迁移用 301 / 308。
robots.txt 不要挡住重要页面,也不要挡住必要的 JS、CSS、图片资源。
不要因为 Cookie、地区、User-Agent 让 Googlebot 看到和普通用户完全不同的内容。
3. 让重要页面可索引
能抓取不等于能索引。
不要误加
noindex。canonical 指向正确的规范 URL。
sitemap、canonical、站内链接尽量指向同一套 URL。
避免大量重复页、参数页、空结果页、薄内容页。
搜索页、用户中心、后台页面通常不应该进索引。
4. 让页面内容可理解
搜索引擎需要理解页面主题和实体关系。
每个重要页面有唯一
title和description。每页有清晰 H1,H2 / H3 表达内容层级。
重要正文、商品信息、文档内容尽量服务端输出。
图片有准确
alt。使用语义 HTML,比如
article、nav、section、table、ul、ol。用 JSON-LD 描述文章、商品、组织、面包屑等实体。
5. 让页面体验足够好
体验不是 SEO 的全部,但会影响搜索表现和转化。
公开内容页优先 SSG / ISR,让首屏更快拿到 HTML。
减少不必要的 Client Component,避免 JS bundle 过大。
首屏图片用
next/image,设置宽高,避免 CLS。字体用
next/font,减少字体闪烁和布局偏移。第三方脚本延迟或按需加载。
关注 Core Web Vitals:LCP、INP、CLS。
总结表:
目标 | Next.js 常用手段 |
|---|---|
可发现 |
|
可抓取 |
|
可索引 | 页面级 robots、canonical、去重、避免薄内容 |
可理解 | Metadata API、语义 HTML、JSON-LD |
可体验 | SSG / ISR / RSC、 |
四、Next.js 落地重点
渲染策略对 SEO 的影响
渲染方式 | SEO 影响 | 适合场景 |
|---|---|---|
SSG | 构建时生成 HTML,爬虫首次请求就能看到完整内容 | 文档、博客、营销页 |
ISR | 静态速度 + 可更新,适合大多数内容型页面 | CMS、商品页、分类页 |
SSR | 请求时生成 HTML,内容新鲜但 TTFB 依赖服务端 | 实时价格、库存、强动态页 |
CSR | 核心内容依赖客户端 JS,SEO 风险更高 | 后台、强交互局部、非 SEO 主体 |
RSC | 服务端执行组件,减少客户端 JS,适合承载内容层 | App Router 默认组件模型 |
面试表达:
公开内容页我会优先考虑 SSG 或 ISR,因为搜索引擎首次抓取时就能拿到完整 HTML。SSR 也可以 SEO,只是更依赖服务端响应和数据源稳定性。CSR 更适合交互局部,不适合承载文章正文、商品详情这类核心 SEO 内容。
Metadata API
固定页面用静态 metadata,动态页面用 generateMetadata。
export const metadata = {
title: '关于我们',
description: '了解 Acme 的产品、团队和使命。',
alternates: {
canonical: '/about',
},
}export async function generateMetadata({ params }) {
const { slug } = await params
const post = await getPost(slug)
return {
title: post.title,
description: post.excerpt,
alternates: {
canonical: `/blog/${post.slug}`,
},
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.ogImage],
},
}
}根布局通常放全站基础配置:
export const metadata = {
metadataBase: new URL('https://example.com'),
title: {
default: 'Acme',
template: '%s | Acme',
},
}注意点:
metadata/generateMetadata只能在 Server Component 中导出。同一个 route segment 不能同时导出
metadata和generateMetadata。metadataBase很重要,否则 canonical、OG image 等绝对 URL 容易出问题。metadata 要和页面正文一致,不能只靠标题伪装内容。
sitemap、robots、canonical
app/sitemap.ts 用于生成 sitemap:
import type { MetadataRoute } from 'next'
const siteUrl = 'https://example.com'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getPublishedPosts()
return posts.map((post) => ({
url: `${siteUrl}/blog/${post.slug}`,
lastModified: post.updatedAt,
}))
}app/robots.ts 用于生成 robots.txt:
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/admin/', '/api/internal/'],
},
sitemap: 'https://example.com/sitemap.xml',
}
}页面级 noindex:
export const metadata = {
robots: {
index: false,
follow: true,
},
}关键判断:
sitemap 放想被索引的规范 URL。
robots.txt 控制抓取,不是控制索引的首选方式。
不想被索引用
noindex。重复内容用 canonical。
URL 迁移用 redirect。
JSON-LD
JSON-LD 用来帮助搜索引擎理解页面实体,不属于 Metadata API。
export default async function BlogPostPage({ params }) {
const { slug } = await params
const post = await getPost(slug)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.excerpt,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
author: {
'@type': 'Person',
name: post.author.name,
},
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, '\\u003c'),
}}
/>
<article>
<h1>{post.title}</h1>
<p>{post.excerpt}</p>
</article>
</>
)
}常见类型:
类型 | 适合页面 |
|---|---|
| 博客、资讯、教程 |
| 商品详情页 |
| 面包屑 |
| 公司、品牌、官网 |
| 站点级信息 |
| FAQ 内容页 |
| 本地商家 |
| 视频内容页 |
原则:
结构化数据必须和页面可见内容一致。
不要伪造价格、库存、评分、评论。
上线前用 Rich Results Test 或 GSC Enhancements 检查。
五、场景策略与排查
不同页面怎么做
页面 | 策略 |
|---|---|
博客文章 | SSG / ISR、动态 metadata、Article JSON-LD、进 sitemap |
文档页 | SSG / ISR、强内链、目录结构、进 sitemap |
商品详情 | ISR / SSR、Product JSON-LD、价格库存真实、进 sitemap |
分类页 | ISR,有独立内容时 index,筛选页谨慎 |
搜索结果页 | 通常 noindex,不进 sitemap |
登录 / 用户中心 | noindex,不进 sitemap,必要时 robots 阻止 |
后台管理 | 鉴权 + noindex + robots,不做 SEO |
旧 URL | 301 / 308 到新 URL,不进 sitemap |
404 页面 | 返回真实 404,不进 sitemap |
常见问题排查
新页面不收录:
是否在 sitemap 中。
是否有站内链接。
URL Inspection 是否可抓取。
是否返回 200。
是否被 robots.txt 阻止。
是否有 noindex。
canonical 是否指向自己。
HTML / rendered HTML 是否有核心内容。
内容是否太薄或重复。
流量下降:
GSC Performance 看下降发生在哪些 query / page。
Pages 看是否索引异常。
URL Inspection 检查重点 URL。
检查 robots / noindex / canonical 是否被改坏。
检查 sitemap 是否丢 URL。
检查最近部署是否把 SSR / SSG / ISR 改成 CSR loading。
检查 Core Web Vitals 是否恶化。
Google-selected canonical 和声明不一致:
内容是否高度重复。
canonical 是否写错。
sitemap 是否提交了另一套 URL。
站内链接是否大量指向非规范 URL。
是否存在 http / https、www / non-www、尾斜杠冲突。
是否应该用 redirect 而不是 canonical。
六、重点面试题
1. Next.js SEO 本质上要解决什么问题?
回答思路:
Next.js SEO 本质上要解决 5 件事:可发现、可抓取、可索引、可理解、可体验。
面试表达:
我不会把 Next.js SEO 理解成只配置 metadata。它本质上要解决五件事:第一是可发现,搜索引擎能通过链接、sitemap、外链找到页面;第二是可抓取,robots、状态码、重定向和鉴权不能挡住重要页面;第三是可索引,页面要有稳定 URL、正确 canonical、可见正文,并避免重复内容;第四是可理解,也就是标题、描述、语义 HTML、结构化数据和内链关系要清晰;第五是可体验,首屏要快,布局要稳定,移动端可用,客户端 JS 不能拖垮渲染。
追问点:
如果面试官继续问 Next.js 的优势,可以接到 SSG / ISR / SSR / RSC:Next.js 能让公开内容页在服务端生成完整 HTML,比纯 CSR 更利于抓取和首屏体验。
2. Next.js 项目里你会怎么做 SEO?
回答思路:
从“五件事”展开,不要只说加 title 和 description。
面试表达:
我会先确定哪些页面应该被索引,比如博客、文档、商品、分类页;哪些页面不应该被索引,比如登录页、用户中心、搜索结果页。对应该索引的页面,我会保证它们有稳定 URL、可被内链和 sitemap 发现、能返回 200、核心内容服务端输出,并通过
generateMetadata配置唯一的 title、description、canonical 和 Open Graph。然后用 JSON-LD 补充文章、商品、面包屑等结构化数据。最后用 GSC 检查索引状态,用 Core Web Vitals 检查真实体验。
可以补充:
在 App Router 中,我会让文章正文、商品信息这类 SEO 核心内容留在 Server Component 中,Client Component 只负责收藏、购物车、弹窗这些交互。
3. SSR、SSG、ISR、CSR 对 SEO 有什么区别?
回答思路:
核心比较点是:首屏 HTML 是否完整、内容更新频率、响应速度和稳定性。
面试表达:
SSG 是构建时生成 HTML,最适合文档、博客、营销页,爬虫首次请求就能看到完整内容。ISR 是静态生成加增量更新,适合 CMS、商品页、分类页这类会变但不要求每次实时的页面。SSR 是每次请求服务端生成 HTML,也可以做 SEO,但 TTFB 和稳定性更依赖服务端和数据源。CSR 如果把核心内容放到浏览器里用 JS 请求后再渲染,初始 HTML 可能只有 loading,对 SEO 风险最大。
追问点:
不是 SSR 才能 SEO。只要搜索引擎能拿到完整、稳定的 HTML,SSG / ISR 往往更适合公开内容页。
4. robots.txt、noindex、canonical、sitemap 的区别?
回答思路:
这题考的是抓取、索引、规范化、发现四个概念边界。
面试表达:
sitemap 是帮助搜索引擎发现 URL,但不保证收录;robots.txt 是控制爬虫是否能访问路径,主要影响抓取;noindex 是明确告诉搜索引擎不要把页面放进索引;canonical 是在重复或相似页面中声明规范 URL;redirect 是 URL 真实迁移,会让用户和爬虫都跳到新 URL。
容易加分:
如果我想让页面 noindex,通常不会用 robots.txt 挡住它,因为爬虫被挡住后可能看不到页面里的 noindex。后台或内部接口可以 robots disallow,但公开重复页更适合 canonical 或 noindex。
5. 为什么纯 CSR 对 SEO 不友好?
回答思路:
不要说“Google 不执行 JS”,更准确是“可以执行,但不应该依赖它承载核心内容”。
面试表达:
纯 CSR 页面初始 HTML 可能只有一个 root 节点或 loading,文章正文、商品信息、主要链接要等 JS 下载、执行、API 返回后才出现。Google 可以渲染 JavaScript,但渲染可能排队,也可能受 JS 错误、接口慢、资源阻塞影响。所以公开 SEO 页面最好让标题、正文、主要链接和结构化数据在服务端输出,客户端 JS 只做交互增强。
Next.js 衔接:
App Router 默认 Server Component,正好适合承载页面主体内容;
use client组件应该尽量局部化。
6. metadata、generateMetadata 和 JSON-LD 怎么用?
回答思路:
固定页面用静态 metadata,动态路由用 generateMetadata,结构化数据用 JSON-LD script。三者都要和页面真实内容一致。
面试表达:
如果页面是固定的,比如 about、pricing,我会用静态
metadata。如果页面依赖路由参数或 CMS 数据,比如/blog/[slug]、/products/[slug],我会用generateMetadata,根据内容生成 title、description、canonical 和 Open Graph。根 layout 里通常放metadataBase、title template 和默认站点信息。JSON-LD 不属于 Metadata API,我会在 page 或 layout 里渲染application/ld+json,比如文章页用BlogPosting,商品页用Product,面包屑用BreadcrumbList。
注意点:
metadata和generateMetadata只能用于 Server Component。同一个 route segment 不能同时导出两者。
动态 metadata 要和页面正文使用同一份内容数据,避免标题和正文不一致。
JSON-LD 必须和页面可见内容一致,不要伪造评分、价格、库存、评论。
7. 一个页面不被 Google 收录,你怎么排查?
回答思路:
按搜索引擎工作流排查:发现、抓取、索引、内容质量。
面试表达:
我会先用 GSC 的 URL Inspection 看它是否在索引中,再用 Test Live URL 看当前是否可抓取。然后检查状态码是不是 200,robots.txt 是否阻止,页面是否有 noindex,canonical 是否指向自己,URL 是否在 sitemap 中,页面有没有站内链接。Next.js 项目里还要看 rendered HTML 或源码中是否有核心内容,避免页面实际变成 CSR loading。最后再判断内容是否太薄、是否重复,修复后再 Request Indexing。
常见追问:
Crawled - currently not indexed说明 Google 抓过页面但没收录,通常要检查内容质量、重复度、页面价值、canonical 和内链,而不是只重新提交 sitemap。Google-selected canonical 和 User-declared canonical 不一致,通常说明 canonical、sitemap、站内链接、重定向或页面内容重复信号不统一。
搜索结果页通常
noindex, follow,因为它容易制造大量低质量 query 页面。
官方入口:
主题 | 地址 |
|---|---|
Next.js Metadata | https://nextjs.org/docs/app/getting-started/metadata-and-og-images |
Next.js sitemap | https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap |
Next.js robots | https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots |
Google Search 工作原理 | https://developers.google.com/search/docs/fundamentals/how-search-works |
Google Search Console |


评论
还没有评论
来发表第一条评论吧!