Next.js 静态导出网页 SEO 优化实践
1 Next.js 静态导出网页 SEO 优化实践指南
在构建极速的现代前端网站时,Next.js 的静态导出 (output: 'export') 是不二之选。它极大降低了服务器算力门槛与运维成本。然而,在剥离了 Node.js SSR 服务器后,如何在完全纯静态的产物下实现国际化 (i18n) SEO?
本文将复盘我在开发 Monticule Tech (丘原科技) 官网时的全套 SEO 优化思路。
1.1 为什么弃用 next-seo?直接拥抱原生 Metadata API
现在,如果你问 AI,在 Next.js 要怎么做 SEO,AI 大概率会告诉你用 next-seo 这个第三方库。我也被 AI 坑了一下,直到我发现,实际上 Next.js 自己就内置了 SEO 工具。
在 Pages Router 时代,开发者往往会依赖 next-seo 等第三方库去强行把 <meta> 标签塞入文档头。但在全新的 App Router 下,Next.js 官方已经内置了一套极其强大的、开箱即用的 Metadata API。
我们只需要在 layout.tsx 或独立的子页面 page.tsx 中导出一个(甚至支持异步的)generateMetadata 函数。这不仅框架运行效率更高、带来零多余客户端打包体积,还能通过树状嵌套天生支持 OpenGraph、Canonical、Hreflang 等高度复杂的标签生成。
1.2 国际化 (i18n) 各子页面的动态独立 TDK 注入
对于 SEO 而言,我们通常需要一个富含长尾关键字组合的描述。
💡 贴士:专业的 SEO 体检报告指出,英文的
meta description强烈建议人工精简压缩至 110 - 160 个字符之间。相比之下,中文因为信息密度高,往往可以更从容地布置关键字。
随后,在每个单独页面的 Server Component 内通过读取字典,拼装出独立的 Title 和 Description:
举个栗子:
// src/app/[lang]/about/page.tsx
export async function generateMetadata({ params }): Promise<Metadata> {
const { lang } = await params;
const dict = await getDictionary(lang);
return {
title: dict.seo.about_title,
description: dict.seo.about_desc,
openGraph: {
title: dict.seo.about_title,
description: dict.seo.about_desc,
// 动态注入社交分享横图
images: ["/your-image-here.webp"],
// ...
}
};
}1.3 多语言站点的痛点:用 Canonical 与 Hreflang 治理索引混乱
当你有一个 /en/about 与一个 /zh/about(甚至还有默认隐藏语言标识的 /about)时,搜索引擎极易将它们判为内容完全雷同的“克隆页面”从而施加重复抓取惩罚。
解决办法是通过 Metadata.alternates 清晰地交代三件事:
这个页面在当前语系下的绝对标准权威地址 (
canonical) 是什么?这个页面在全球范围内,默认推荐访客进入的大门 (
x-default) 在哪?你的另外几个“双胞胎异父兄弟”(替代语言
hreflang)分别位于哪个网址?
// 在 generateMetadata 的返回值中:
alternates: {
canonical: "/zh/about/", // 指示百度/Google这是源自何处的唯一“干净链接”
languages: {
"x-default": "/about/", // 提供给无法匹配到相关语言的全球过客
"en": "/about/",
"zh": "/zh/about/",
},
}
1.4 防不胜防:彻底消灭静态导出的 301 重定向陷阱 (Trailing Slashes)
在用专业的 SEO 工具跑了一遍全站深度抓取后,我发现遇到了大量的警告:301 Redirect in sitemap。
1.4.1 根本原因在哪里?
如果你使用了 output: 'export' 将 Next.js 项目编译成了静态的 HTML 并打包送上服务器(如 Nginx、CDN 等)。从物理文件层面来说,Next 会将路由构建成一个个包裹着 index.html 的文件夹(例如:out/products/index.html)。
当爬虫顺着常规不带斜杠的代码链接去强行访问 /products 时,底层宿主静态服务器判定它是目录,并会自动下发一个 HTTP 301 Moved Permanently 的指令,要求来宾再跳转跑一遍带物理斜杠的 /products/。这对于惜时如金的爬虫抓取预算 (Crawl Budget) 是完全多余的性能浪费,甚至削弱 SEO 评分。
1.4.2 解法:全部使用尾部斜杠 (Trailing Slash)
改写
<Link>:将页面内诸如 Navbar 与大按钮里所有的跳转<Link href="/products">修改为严格精确的<Link href="/products/">。改写元数据:如上一节中所示,确保
canonical与openGraph.url指向出去的数据全部显式携带/结尾。改写 Sitemap:主动修改你的站点地图配置文件,确保主动提报给爬虫们的网址列表,更是天生就自带无损斜杠的。
1.5 最后一块拼图:生成 Sitemap 与 Robots
Next.js v13 新增了优雅的 sitemap.ts 与 robots.ts 生成规约,但在开启了完全静态输出(output: export)时,如果这两个特殊文件里包裹了基于请求的动态生成意图,npm run build 命令行会在数据抓取时直接抛出错崩溃:
Error: export const dynamic = "force-static"/export const revalidate not configured on route "/robots.txt" ...
一步到位的解决:直接在包含映射表的 sitemap.ts 和 robots.ts 文件最最最首行代码处,直接加入强制静态的系统级申明锁:
// src/app/sitemap.ts
export const dynamic = "force-static"; // <- 必须在第 1 行!
import { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
const routes = ['', '/about/', '/products/', '/zh/', '/zh/about/', '/zh/products/'];
return routes.map((route) => ({
url: `https://monticule.tech${route}`,
lastModified: new Date().toISOString(),
// ...
}));
}这样子,静态构建时它们就会直接在根目录下为你产出标准的 sitemap.xml 和 robots.txt 平面文件。
1.6 总结
不必盲目搬弄各种沉重的第三方库。深入吃透 Next.js App Router 的自带特性,把国际化字典剥离出专门的高能 SEO 字段落点、安排稳妥的 OpenGraph 横图、顺应静态服务物理特性的尾部斜杠体系,在最后补上一把 force-static 的编译锁。
你的纯静态导出的 Web 应用,也完全能享受到地表最满分的搜索引擎挚爱评分。
声明:本文由 AI 辅助生成
- 感谢你赐予我前进的力量

