WordPress 换成静态站:URL 保住、短代码清洗与五件 SEO 必做

把 WordPress 换成静态站点,是我这几年做过的最划算的一次迁移。不是因为 WordPress 不好,而是我的站已经不需要它的动态能力了:没有会员、没有评论互动、没有电商,每天最重的负载就是首页和文章页的查询。换成静态 HTML 之后,服务器从 2 核 4G 降到 1 核 1G,首页响应时间从 300 毫秒变成 40 毫秒,而且再也不用担心 PHP 版本升级把插件搞挂。

但这件事不能只看结果。迁移动辄涉及上千条 URL、几十个插件留下的短代码、还有一批在搜索引擎里有历史权重的页面。这篇文章把整个过程拆成五步,重点讲那些「迁完才发现」的问题。

一、先判断:你到底该不该迁

静态化不是万能药。以下三类站点迁过去会很痛苦,建议别动:有用户体系(登录、收藏、会员),有频繁交互(评论、评分、站内通知),内容更新频率极高且需要实时生效(比如每小时几十篇的资讯站,构建队列会一直积压)。

反过来,这三种情况迁过去收益最大:内容以长文为主、更新频率每天几篇到十几篇;流量主要是搜索引擎和直接访问;服务器资源紧张或者不想再维护 PHP 环境。我的站属于第三种,文章的「半衰期」很长,两年前的教程现在还在带流量。

还有一个常被忽略的收益:安全性。WordPress 被扫的后台、插件漏洞、xmlrpc 爆破,在纯静态站上全部消失。服务器上只剩一个 Nginx,攻击面小到几乎不用管。

二、先导出,再谈工具选型

我见过太多人第一步就去挑生成器,挑了两周还没动手。真实的顺序应该是:先把 WordPress 的数据完整导出来,看清楚里面有什么,再决定用什么工具。

导出要分四部分,缺一不可:

1. 数据库。mysqldump 全量导出,注意加 --default-character-set=utf8mb4,否则中文会变问号。2. 上传目录。整份 wp-content/uploads 打包,别只拷「文章里引用到的图」——你永远不知道哪张图是在 CSS 里引用的。3. 主题与插件配置。哪怕不用了也留一份,短代码的语义得从插件源码里推断。4. 重定向规则、robots.txt、sitemap 的现状。这三样决定了迁移后搜索引擎能不能顺利跟上。

导完之后先做一次 post 表的全量检查:

SELECT ID, post_name, post_status, post_type,
       LENGTH(post_content) AS len, post_date
FROM wp_posts
WHERE post_type = 'post' AND post_status = 'publish'
ORDER BY post_date DESC;

重点看三件事:post_name 为空的有几条(这些是没有固定链接的,迁移后会丢 URL)、len 异常小的有几条(可能是占位文章)、post_type 除了 post/page 还有哪些(产品、作品集之类的自定义类型很容易在迁移时被漏掉)。

我这次导出来发现两个问题:一是十九篇文章的 post_name 是中文,URL 里是一串百分号编码;二是有一批文章的内容里有 [gallery] 短代码,直接转 Markdown 会变成字面文本。

三、工具选型:静态生成器不是重点

我最后用的是 Hugo,理由很朴素:单二进制、构建快、不用担心 Node 依赖树。Astro 也很合适,如果站点有较多交互组件(搜索、点赞),Astro 的岛屿架构更自然。Hexo 生态老、主题多,但 Node 版本升级时容易踩坑。

但工具选型其实是最不重要的一步。真正的功夫在两件事上:URL 结构要保住,内容要洗干净。工具随便换,这两件事做错了就得重来。

URL 上我的原则是「尽量与原 URL 完全一致」。WordPress 的固定链接是 /archives/1234/ 这种形式,Hugo 默认是 /posts/slug/。虽然可以用 301 重定向,但重定向会损失一点权重,而且几千条重定向规则会让 Nginx 配置臃肿。如果能直接生成同样的路径,就完全不用重定向。Hugo 里可以给每篇文章设置 url front matter 来强制指定输出路径,我在导出脚本里直接把 WordPress 的 post_name 和 ID 填进去,一个不差。

四、内容清洗:短代码、图片与内链

这是整个迁移里最耗时的部分,也是唯一不能自动化的部分。WordPress 的内容是「HTML + 短代码」的混合体,走向另一头有两条路:转成 Markdown,或者保留原样 HTML。

我选了保留 HTML。原因是我的文章里有大量 <pre><code> 代码块,转 Markdown 时缩进、特殊字符、行内代码的边界很容易出错,而且转完还得肉眼校对一遍,成本比重写还高。静态生成器本来就支持在 Markdown 文件里写原生 HTML,直接贴进去最保险。

短代码必须逐个处理。我写了个脚本把所有不同的短代码提取出来统计:

grep -ohE '\[[a-z_]+[^]]*\]' /tmp/wp_export/*.html \
  | cut -d' ' -f1 | sort | uniq -c | sort -rn

结果里有 [caption](图注)、[gallery](相册)、[embed](嵌入视频)、[code](代码块)。前三个我全部手工改写成对应的 HTML 片段——[caption] 改成 <figure> 加 <figcaption>,[gallery] 改成一组图片的 <div>,[embed] 直接改成 iframe。只有 [code] 数量多(三百多处),用脚本批量替换成 <pre><code>。

图片路径也要处理。WordPress 的图片在 /wp-content/uploads/2024/03/xxx.jpg,静态站一般放 /images/ 下。这里有个坑:如果直接改路径,会有一批图片因为大小写不一致而 404(Linux 文件系统区分大小写,而 WordPress 上传时可能保留了扩展名大写)。我是在迁移完成后跑了一次全站链接检查才发现的,一共有十一张图挂了。

内链同样要扫。WordPress 里很多内链是绝对地址 https://example.com/archives/1234/,如果改了域名或者 URL 结构,这些链接全部失效。批量替换成相对路径更稳,但如果站要换域名,绝对路径反而方便统一替换。我的选择是换成根相对路径 /archives/1234/,这样无论换域名还是上 CDN 都不受影响。

五、评论怎么办:三个选项,各有代价

静态站没有后端,评论必须交给第三方。我把三个方案都试过:

Disqus 最省事,但加载慢、在国内基本不可用,而且它会往页面里塞一堆第三方脚本,把好不容易优化出来的性能吃回去。放弃。

自建评论服务(比如用 Serverless 函数加数据库,或者用开源的 Waline、Twikoo)。可控性最好,但意味着你又维护了一个后端——虽然比 WordPress 轻得多,但「纯静态」的初衷就打了折扣。

直接关掉评论。我最后选了这个。原因很实在:我的文章是技术教程,读者有问题会发邮件或者在社交媒体上问,评论区里大部分是垃圾外链。关掉评论后,不仅省了维护成本,页面上少了几十 KB 的 JS,也顺带减少了被灌水攻击的风险。

关评论之前我导出并保留了所有历史评论,转成了纯文本存档。不显示不代表要丢掉,那些内容以后写文章时还能用上。

六、SEO 迁移:五件必须做的事

这一步做不好,前面所有努力白费。迁移后流量掉一半的案例我见过不少,全是这里出的问题。

第一,URL 映射表。先导出一份「旧 URL → 新 URL」的完整对照表。如果新路径与旧路径完全一致,这张表就是空的,皆大欢喜。如果有差异,必须在 Nginx 里配 301,而且要用 map 而不是逐条写 location——几千条 location 会让 Nginx 启动变慢、配置难以维护。

第二,sitemap 重建。WordPress 的 sitemap 是插件生成的,迁移后要用生成器自带的或者自己写一个。lastmod 必须是真实的内容修改时间,不能统一写成构建时间——否则每次构建所有页面的 lastmod 全变,搜索引擎会认为你在刷,直接降低对这个信号的信任度。

第三,canonical 与结构化数据。每篇文章的输出必须有自引用的 canonical。结构化数据(Article、BreadcrumbList)要跟着迁移,WordPress 插件生成的那份 JSON-LD 可以照抄结构,但要把作者、发布时间、修改时间对上。

第四,robots.txt 与 404。静态站的 404 页面要返回真正的 404 状态码,而不是 200 —— 有些静态托管平台的「单页应用回退」配置会让所有未匹配路径返回 index.html 且状态码 200,这会造成大量软 404,把搜索引擎搞晕。Nginx 上用 try_files $uri $uri/ =404 最稳。

第五,提交与观察。迁移完成后立刻在 Search Console 提交新 sitemap,然后用「网址检查」抽几个重点页面确认能被抓取。接下来两周每天看一次「覆盖率」和「抓取统计」,重点关注 404 数量激增和「已发现但未编入索引」的变化。

七、部署:从「上传到服务器」到「一条命令」

静态站最容易做错的是部署环节。我最初的方案是本地构建完,用 rsync 同步到服务器。这个方案能用,但有两个问题:每次同步都要考虑 --delete 会不会误删东西;本地和服务器状态容易漂移。

现在的做法是:构建产物进 Git 仓库(或者放进一个独立的分支),服务器上 git pull,然后由 Nginx 直接指向那个目录。这样每次发布都是「提交、拉取、完成」,没有增量同步的不确定性,回滚也只需要 git checkout 到上一个提交。

cd /var/www/blog
git fetch origin main
git reset --hard origin/main
nginx -t && systemctl reload nginx

如果站点规模更大,应该走 CI:推送触发构建,产物同步到对象存储,CDN 回源。但单机小站用不上那套,一行脚本已经足够可靠。

还有一点:静态站的文件权限要理干净。产物目录属于部署用户,Nginx 只需要读权限,不要图省事给 777 —— 静态站最容易出事的就是「某个目录被写入了不该有的文件」,虽然它不会执行,但可以被当成钓鱼页面的宿主。

八、迁移后的核对清单

迁完不是结束,我用下面这份清单核对了整整三天:

文章总数是否一致(数据库计数 vs 生成的文件数);每篇文章的标题、发布时间、分类是否正确;所有图片可访问(用站点地图爬一遍,看有没有非 200);所有内链可点击(同上);旧 URL 是否 301 到新 URL;首页与前 N 页的分页是否正常;RSS 输出是否有效(用 W3C 校验器);Search Console 的 sitemap 是否被成功读取;移动端是否正常渲染;页面加载时间是否真的变快了(这个要实测,不能想当然)。

那次核对里我抓到了三个问题:一篇 2019 年的文章因为没有 post_name 被跳过没生成;RSS 里的日期格式不符合 RFC 822 导致校验失败;以及一个分类页的计数把草稿也算进去了。都不是大问题,但如果不核对,会一直留在那里。

最后说一句判断:如果你的站已经稳定运行三年以上、内容以长文为主、又厌倦了每次升级 WordPress 都要提心吊胆,那静态化值得做。但请留出一整个周末,别指望两个小时搞定——磨刀不误砍柴工,慢一点比返工快得多。

Last modification:November 12th, 2026 at 10:24 pm

Leave a Comment