Next.js 提供了做技术 SEO 所需的一切,但几乎没有一项是默认开启的。这是我在每个 App Router 项目上都会过一遍的清单,大致按影响大小排序。
1. 重要内容用服务端渲染
搜索引擎能执行 JavaScript,但对服务端渲染的 HTML 收录得更快、更稳定。在 App Router 中,服务端组件是默认选项——把标题、正文和链接放在服务端组件里,客户端组件只负责交互,不负责内容。
快速检测:禁用 JavaScript 打开页面,或者运行 curl https://yoursite.com/page。如果响应里没有主要文字,第一轮收录里也不会有。
2. 设置 metadataBase 和标题模板
在根布局中:
import type { Metadata } from 'next'
export const metadata: Metadata = {
metadataBase: new URL('https://example.com'),
title: {
default: 'Example Studio — Web Design and Development',
template: '%s — Example Studio',
},
description: 'Websites and products for small teams: design, development and launch.',
openGraph: { siteName: 'Example Studio', type: 'website' },
}metadataBase 会把相对 URL(canonical、Open Graph 图片)转成绝对 URL。有了标题模板,子页面只需设置自己那部分标题。
3. 每个页面都有独立的标题和描述
每个需要被收录的页面都要有自己的 <title>(约 50–60 个字符,关键词靠前)和 meta description(约 150–160 个字符,写得让人想点击)。动态页面使用 generateMetadata:
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await getPost(slug)
if (!post) return {}
return {
title: post.title,
description: post.excerpt,
alternates: { canonical: `/blog/${post.slug}` },
openGraph: {
type: 'article',
publishedTime: post.date,
images: [{ url: post.cover, width: 1200, height: 630 }],
},
}
}4. Canonical 链接
每个页面都设置 alternates.canonical。它能防止查询参数(?utm_source=...)、末尾斜杠和备用域名造成的重复内容。选定一种形式——带不带 www、带不带末尾斜杠——然后在托管平台把其他形式重定向过来。
5. Sitemap
app/sitemap.ts 会生成 /sitemap.xml,静态导出也支持:
import type { MetadataRoute } from 'next'
import { getAllPosts } from '@/lib/blog'
export const dynamic = 'force-static'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const base = 'https://example.com'
const posts = await getAllPosts()
return [
{ url: base, changeFrequency: 'monthly', priority: 1 },
...posts.map((post) => ({
url: `${base}/blog/${post.slug}`,
lastModified: post.date,
changeFrequency: 'yearly' as const,
priority: 0.7,
})),
]
}在 Google Search Console 和 Bing 网站管理员工具中各提交一次。如果主要面向国内用户,也别忘了百度搜索资源平台。
6. robots.txt
import type { MetadataRoute } from 'next'
export const dynamic = 'force-static'
export default function robots(): MetadataRoute.Robots {
return {
rules: { userAgent: '*', allow: '/' },
sitemap: 'https://example.com/sitemap.xml',
}
}不要用 robots.txt 来阻止页面被收录——被屏蔽的页面仍可能通过外链被收录。应该在页面的 metadata 中使用 robots: { index: false }。
7. 结构化数据(JSON-LD)
结构化数据帮助搜索引擎理解页面是什么。对于文章,在页面中渲染一个 script 标签:
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.excerpt,
datePublished: post.date,
image: `https://example.com${post.cover}`,
author: { '@type': 'Person', name: 'Jane Doe', url: 'https://example.com' },
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, '\\u003c') }}
/>
)对小型企业有用的类型:首页用 Organization 或 Person,文章用 BlogPosting,深层页面用 BreadcrumbList,商品页用 Product。用 Google 的富媒体搜索结果测试工具验证。
8. Open Graph 图片
在领英、X、Slack 和各种聊天软件里分享链接时,会用到 Open Graph 标签。给每个页面一张 1200×630 像素的图片。可以在页面旁边放一个静态的 opengraph-image.png,也可以在 opengraph-image.tsx 中用 ImageResponse 生成(静态导出时在构建阶段生成,所以不能依赖请求信息)。
9. 多语言网站:真实的 URL 和 hreflang
每种语言都需要自己的 URL,否则搜索引擎永远只能看到其中一种。用 localStorage 在客户端切换语言对访客没问题,但 Google 看不到。使用语言路由段——app/[lang]/...——并声明各语言版本:
alternates: {
canonical: '/en/blog/my-post',
languages: {
en: '/en/blog/my-post',
'zh-CN': '/zh/blog/my-post',
'sr-Latn': '/sr/blog/my-post',
'x-default': '/en/blog/my-post',
},
}每个语言版本都应列出所有其他版本,包括它自己。
10. 内部链接和标题层级
- 每页一个
<h1>,然后按顺序使用<h2>/<h3>。标题是大纲,不是字号。 - 相关页面之间互相链接,并使用描述性的锚文本("静态导出指南",而不是"点击这里")。使用
<Link>,确保它们是真正的<a href>元素。 - 重要页面离首页不超过三次点击。
11. Core Web Vitals
速度是排名信号,更重要的是转化信号。用 PageSpeed Insights 测量(实地数据来自真实的 Chrome 用户),目标是 LCP ≤ 2.5 秒、INP ≤ 200 毫秒、CLS ≤ 0.1。App Router 项目中常见的问题:首屏图片过大、客户端 JavaScript 过多,以及字体或未设尺寸的图片造成的布局偏移。如果你的网站动画很多,可以看动画网站的 Core Web Vitals 优化。
12. 图片
- 内容图片写描述性的
alt,装饰性图片用空的alt=""。 - 明确的
width/height或aspect-ratio,防止布局偏移。 - 使用现代格式(AVIF、WebP)和合适的尺寸。
- 描述性的文件名:
nextjs-sitemap-example.png,而不是IMG_2041.png。
13. 404 和重定向
对不存在的页面返回真正的 404(App Router 中用 notFound())。移动或重命名页面时,从旧 URL 加一个 301 重定向——在静态导出中,就是托管平台的 _redirects 规则。
精简版清单
- 内容放在服务端组件中
- 根布局中设置
metadataBase和标题模板 - 每个页面都有独立的标题、描述和 canonical
-
sitemap.ts和robots.ts,并提交 sitemap - 文章、组织、面包屑的 JSON-LD
- 每个页面都有 Open Graph 图片
- 每种语言一个 URL,并配置 hreflang
- 合理的标题层级和描述性内部链接
- Core Web Vitals 全部达标
- 真正的 404,以及移动页面的 301
在做静态网站?阅读 Next.js 静态导出完全指南,了解没有服务器时哪些地方不一样。

