wordpress-to-nextjs-migration-seo.html
< BACK 一张陈旧的卡片目录放在昏暗的档案室里,温暖的琥珀色光线照亮了散落的索引卡

将 WordPress 迁移到 Next.js 而不损失 SEO

三年前,我接手了一个客户的博客——400 篇文章、月均 6 万自然访问量、八年积累的 PageRank——并将其迁移到一个闪亮的 Next.js 前端。六周内我们就失去了 34% 的流量。不是因为新网站速度慢。不是因为内容消失了。而是因为我在四个具体的地方处理不当,我会在这篇文章中逐一说明,这样你就不会重复我的错误。

Headless WordPress with Next.js 对性能和开发者体验来说确实出色。但如果你的规范标签错了、XML 站点地图指向旧域名、结构化数据在 WPGraphQL 和 getStaticProps 之间消失了,Google 才不在乎你的 Lighthouse 分数。迁移本身才是危险的部分。做对它主要取决于纪律,不是魔法。getStaticProps. The migration itself is the dangerous part. Getting it right is mostly about discipline, not magic.

---

为什么这次迁移首先会破坏 SEO

大多数教程都略过了一件事:WordPress 为你做了大量 SEO 工作,而你却没有意识到。Yoast 或 Rank Math 生成你的元标签。WordPress 核心处理你的永久链接结构。你的主题可能输出某种 schema 标记。你的 XML 站点地图每次发布时都会自动重新生成。some kind of schema markup. Your XML sitemap auto-regenerates every time you publish.

当你通过 WPGraphQL API 将内容层拉入 Next.js 并从新的前端服务时,所有这些基础设施都成了你的问题。每。一。个。WPGraphQL API and serve it from a new front-end, all of that infrastructure becomes your problem to replicate. Every. Single. Piece.

另一个问题是 URL 结构。大多数 WordPress 网站使用 /category/post-slug/ 或 /year/month/post-slug/ 或仅仅 /post-slug/。Next.js 给了你一个空白的路由画布。如果你规划得不仔细,那个空白画布就成了排名的墓地。/category/post-slug/or/year/month/post-slug/or just/post-slug/. Next.js gives you a blank canvas for routing. That blank canvas is a ranking graveyard if you don't plan it carefully.

我经常看到的两种失败模式

首先是迁移 URL 的团队仍然会出问题——通常是因为重定向应用不一致,或者新的 sitemap 在重定向生效之前就上线了。其次是有意改变 URL 结构的团队(通常是为了清理),把重定向映射当成事后考虑。两者都是可以修复的。但都不应该出现。

---

在触及任何东西之前先进行审计

在写任何一行 Next.js 代码之前,先完成 URL 清单。我用 Screaming Frog——爬取实时 WordPress 站点,导出每个可索引的 URL,然后全部导入电子表格。对于 400 页的网站,这可能需要一小时。对于 4,000 页的网站,还是只需要一小时,因为工具会自动处理。Screaming Frog -- crawl the live WordPress site, export every indexable URL, and dump it into a spreadsheet. For a 400-page site that's maybe an hour of work. For a 4,000-page site it's still just an hour, because the tool does it automatically.

你要捕获的内容:

  • 每个当前已索引的规范 URL
  • 每个的 HTTP 状态(识别已存在的 404 和 301)
  • 每个页面的元标题和描述
  • 哪些页面具有结构化数据(使用富媒体搜索结果测试或直接查看源代码)
  • 站内入站链接——这样你就知道哪些页面链接到哪些页面

还要从Google Search Console拉取前50个页面,按点击次数排序。这些是你不能出错的页面。在电子表格中标记它们。像对待生产依赖一样对待它们。

Seahawk 在 2022 年末接手了一个电商客户——1,200 产品的 WooCommerce 店铺要迁移到无头架构。我们在写代码之前花了整整两天做审计。客户以为我们在浪费时间。结果我们救回了他们月均 9 万次自然会话。

---

将WordPress设置为真正的无头CMS

这部分基本上很直接。安装WPGraphQL并通过GraphQL API暴露你的内容。但有几件事值得认真考虑。

在WordPress端保持Yoast(或Rank Math)运行

即使你不再用 WordPress 作为公开前端,也要保持 SEO 插件活跃。WPGraphQL for Yoast SEO(或相应的 Rank Math 扩展)通过 GraphQL API 直接公开所有 SEO 元数据——标题、描述、规范 URL、OG 数据、robots 指令。这意味着你可以从 Next.js 查询它,并完全按照 Yoast 的意图来渲染。WPGraphQL for Yoast SEO(or the equivalent Rank Math extension) exposes all the SEO meta -- titles, descriptions, canonical URLs, OG data, robots directives -- directly through the GraphQL API. That means you can query it from Next.js and render it exactly as Yoast intended.

这是我之前提到的 2019 年流量下降事件的教训。我原以为可以在 Next.js 中从文章标题 + 网站名称重新生成标题。我们确实可以。但 Yoast 为大约 80 篇表现最好的文章手动自定义了 meta 标题,我们把所有这些都删除了。花了八周才恢复。

谨慎关闭 WordPress 前端

一旦你准备好将流量转向 Next.js,你不能让 WordPress 同时服务它自己的前端。大规模重复内容。最干净的方法是在 WordPress 安装上设置 robots.txt 为 Disallow: /,同时你的 Next.js 站点上线,然后最终防火墙 WordPress URL,使其只能在内部或通过 VPN 访问。robots.txt on your WordPress install to Disallow: /while your Next.js site goes live, then eventually firewall the WordPress URL entirely so it's only accessible internally or via VPN.

不要跳过 robots.txt 这一步。我见过团队在 CDN 级别阻止 WordPress,然后发现 Googlebot 有一个缓存路由。需要几个月时间才能清理干净。

---

完全复制 URL 结构

我的强烈默认建议:保持你的 URL 完全相同。相同的 slug,相同的永久链接结构,相同的末尾斜杠行为。Next.js 路由越接近 WordPress 路由,你需要的重定向就越少,你承担的风险就越小。keep your URLs identical. Same slug, same permalink structure, same trailing slash behaviour. The closer the Next.js routes mirror the WordPress routes, the fewer redirects you need, and the less risk you carry.

Next.js 动态路由让这很容易。如果你的 WordPress 文章放在 /blog/[slug],创建 pages/blog/[slug].js。完成。dynamic routes make this easy. If your WordPress posts live at/blog/[slug], create pages/blog/[slug].js. Done.

麻烦的地方在于分类存档、作者页面、标签页面和分页存档(/blog/page/2/)。WordPress 会自动生成所有这些。在 Next.js 中你需要自己构建。很多团队会搁置这些,然后疑惑为什么爬虫覆盖率下降了。/blog/page/2/). WordPress generates all of these automatically. In Next.js you're building them yourself. A lot of teams deprioritise these and then wonder why crawl coverage dropped.

这是我的 URL 对等检查清单(按编号):

  1. 单个文章/页面——完全匹配 slug,包括任何子文件夹 -- match the slug exactly, including any subfolder
  2. 分类归档——用 getStaticPaths 从 WPGraphQL 拉取所有分类来重新创建 /category/[slug]/ -- recreate/category/[slug]/with getStaticPaths pulling all categories from WPGraphQL
  3. 标签存档 -- 同上,如果它们有自然流量就别跳过 -- same as above, don't skip these if they get organic traffic
  4. 作者存档 -- 先检查Search Console;如果没有点击,可以用301重定向到首页 -- check Search Console first; if they get zero clicks, you can 301 them to the homepage
  5. 分页存档 -- /blog/page/[num]/如果你有很多文章,保留它是值得的 -- /blog/page/[num]/is worth preserving if you have a lot of posts
  6. 附件页面 -- 几乎总是用301重定向到父文章;它们在WordPress中也是SEO的累赘 -- almost always 301 these to the parent post; they're SEO dead weight in WordPress too
  7. Feed URL -- /feed/应该301重定向到你的新RSS feed,如果没有就返回410 -- /feed/should 301 to your new RSS feed if you have one, or return 410 if not

---

重定向:被所有人低估的部分

如果你要改变任何URL -- 我会不赞成,但有时确实必要 -- 你的重定向映射需要在上线前建立好,并在staging环境中测试are changing any URLs -- which I'd push back on, but sometimes it's necessary -- your redirect map needs to be built before launch and tested in a staging environment.

在Next.js中,重定向写在next.config.js里。对于小网站(少于200个重定向)没问题。对于更大的情况,把它们放在JSON文件里并导入,或者用middleware动态处理。Vercel的edge middleware很棒,因为它在页面渲染前运行 -- 零延迟开销next.config.js. For small sites (under 200 redirects) that's fine. For anything larger, put them in a JSON file and import it, or use middleware to handle them dynamically.Vercel's edge middleware is excellent for large redirect tables because it runs before the page is rendered -- zero latency penalty.

next.config.js 中的格式:next.config.js:

``redirects: [ { source: '/old-slug', destination: '/new-slug', permanent: true } ]``redirects: [ { source: '/old-slug', destination: '/new-slug', permanent: true } ]``

permanent: true发送301。用于所有真正的URL改变。别用302(临时)除非你真的打算还原 -- Google对它们的对待方式很不一样 sends a 301. Use it for all genuine URL changes. Don't use 302 (temporary) unless you actually intend to revert it -- Google treats them very differently.

在上线前测试每个重定向。我用一个简单的 bash 脚本遍历电子表格,用 curl 检查每个旧 URL 是否返回 301 状态码到正确的目标地址。写好脚本需要十分钟,能省去上线后数小时的崩溃。

---

Next.js 中的 Meta 标签、规范 URL 和结构化数据

大多数迁移在这里悄悄失分。内容在那里,URL 能用,但 SEO 信号错了。

Meta 标签

使用next-seo。这是标准做法。把从WPGraphQL Yoast查询到的数据传给它。你的_app.js有一个DefaultSeo配置,每个页面有一个NextSeo组件处理特定页面的覆盖。直接从Yoast GraphQL响应中提取标题、描述、OG标题、OG图片、规范URL和robots指令 -- 别重新发明轮子next-seo. It's the standard. Pass it the data you've queried from WPGraphQL Yoast. Your_app.js gets a DefaultSeo config, and each page gets a NextSeo component with the page-specific overrides. Take the title, description, OG title, OG image, canonical URL, and robots directives directly from the Yoast GraphQL response -- don't reinvent them.

有一个容易被人忽略的东西:规范网址(canonical URL)。在WordPress中,Yoast会自动设置规范网址。在Next.js中你需要显式传入规范网址。如果你忘记了,Next.js会渲染出没有规范标签的页面,如果你的任何地方都有查询字符串(分页、筛选),你会比预期更快地遇到重复内容问题。

结构化数据

WordPress 主题和插件通常会自动输出 JSON-LD。在无头 CMS 中这些会消失。你需要重新构建它。对于文章,使用 Article schema。对于产品,使用 Product schema。对于本地商家,使用 LocalBusiness schema。我把这些写成接受 props 并返回 <script type="application/ld+json"> 标签的 React 组件。每个 schema 类型一个组件,在整个应用中重用。Article schema. For products,Product. For local businesses,LocalBusiness. I write these as React components that accept props and return a<script type="application/ld+json">tag. One component per schema type, reused across the app.

在迁移前用富媒体搜索结果测试工具检查你之前有过的每个 schema 类型。记录下来。重新创建它们。在上线后用同一工具测试新的。before migration. Document them. Recreate them. Test the new ones with the same tool post-launch.

XML网站地图

别用静态sitemap。动态生成它。对于小网站,getServerSideProps在/sitemap.xml路由上可行。对于有数千篇文章的大型网站,通过自定义脚本在构建时生成sitemap并输出到public/文件夹。Vercel在每次部署都会运行这个 -- 你的sitemap始终是最新的getServerSideProps on a/sitemap.xml route works. For large sites with thousands of posts, generate the sitemap at build time via a custom script and output it to the public/folder. Vercel runs this at every deployment -- your sitemap is always current.

在新网站上线的第一天将新的网站地图URL提交给Google Search Console。不是第三天。第一天。

---

上线后监控(90天窗口期)

迁移不会在上线时结束。它会在你的排名稳定下来时结束——根据Google的文档,这可能需要几周到几个月,取决于爬虫预算和网站权重。

我每个工作日在第一个月要看的东西:

  • Google Search Console → Coverage 报告,查找新的 404 或不应被排除的"Excluded"网址 for new 404s or 'Excluded' URLs that shouldn't be excluded
  • Search Console → Performance——逐周比较你排名前50的页面的点击数和展示数 -- compare clicks and impressions week-on-week for your top 50 pages
  • 用 Screaming Frog 重新爬取新网站,捕捉任何内部 404 或配置错误的规范标签 of the new site to catch any internal 404s or misconfigured canonical tags
  • Core Web Vitals——没错,Next.js网站应该会更快,但要在现场数据(CrUX)中验证,而不仅仅是Lighthouse -- yes, the Next.js site should be faster, but verify it in the field data (CrUX), not just Lighthouse

如果你在前两到三周看到明显下降,不要立即惊慌。随着Google重新爬取和重新索引,几乎总会有短期波动。你要找的是第四周之后持续的下降。这才是表明结构出问题的信号。

回到2022年末——这是一个不同于电商项目的项目——我们为一个SaaS博客推出了Next.js迁移,结果在第二周看到展示数下降了20%。原来我们动态生成的网站地图包含了noindex页面,因为我们没有正确过滤WPGraphQL查询。四小时内修复了。排名在三周内恢复了。监控在问题扩大前就捕捉到了。noindex pages because we hadn't filtered the WPGraphQL query properly. Fixed it in four hours. Rankings recovered in three weeks. The monitoring caught it before it compounded.

---

常见问题

WordPress迁移到Next.js需要多长时间?

说实话,这取决于网站复杂度而不是文章数量。一个100页的简洁URL宣传网站可以在两到三周内正确完成。一个有2000篇文章、自定义文章类型、ACF字段和WooCommerce集成的博客,如果你同时要做好SEO工作和开发,最少需要六到八周。别让任何人告诉你这是一个周末就能完成的活。

我应该在Next.js中使用Pages Router还是App Router?

从2024年中期开始,我对新项目默认使用App Router。但如果你的团队更熟悉Pages Router,而且这是一个时间敏感的迁移,那就用你熟悉的。SEO影响是最小的——两者都支持静态生成、服务端渲染和动态路由。next-seo包现在也支持App Router了。next-seo package has App Router support now too.

我是否需要完全放弃WordPress主机?

不。WordPress可以保持在现有的主机上——WP Engine、Kinsta、Cloudways,你正在使用的任何主机——纯粹作为内容API。Next.js前端部署到Vercel或Netlify。两者通过HTTP通信。有些客户实际上更喜欢这种方式,因为编辑团队可以继续使用他们已经熟悉的WordPress后台。Netlify. The two communicate via HTTP. Some clients actually prefer this because the editorial team keeps the WordPress admin they already know.

那些影响SEO的WordPress插件呢——比如在Redirection中管理的重定向?

在迁移之前导出它们。Redirection插件有CSV导出功能。把所有现有的重定向导入到你的next.config.js或edge中间件中。不要假设它们会自动保留——不会的,因为它们存在于WordPress数据库中,Next.js根本不知道它们的存在。Redirection plugin has a CSV export. Take all those existing redirects and add them to your next.config.js or edge middleware. Don't assume they'll carry over automatically -- they won't, because they live in the WordPress database and Next.js has no idea they exist.

不管怎样我的谷歌排名都会下降吗?

几乎总会有短期的波动。一次执行良好的迁移,零URL变化、正确的重定向(需要时)、复制的元数据和结构化数据,以及重新提交的网站地图,应该在四到六周内稳定下来。我见过持续数月的下降都是由特定的技术错误引起的——不是由迁移本身引起的。

---

迁移本身不是困难的部分。困难的部分是有纪律地完成每一个枯燥、不起眼的步骤——审计、重定向映射、schema重建——然后才是写任何巧妙的Next.js代码。把这个顺序搞对,你就会得到一个更快的前端和相同的排名。一旦Core Web Vitals的改进开始发挥作用,排名可能会更好。Core Web Vitals improvements feed through.

< BACK