惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cyber Attacks, Cyber Crime and Cyber Security
Cisco Talos Blog
Cisco Talos Blog
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
T
Tenable Blog
PCI Perspectives
PCI Perspectives
Simon Willison's Weblog
Simon Willison's Weblog
N
News and Events Feed by Topic
Know Your Adversary
Know Your Adversary
S
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
P
Privacy International News Feed
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
SecWiki News
SecWiki News
S
Securelist
D
Docker
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
T
Tailwind CSS Blog
T
Troy Hunt's Blog
The Register - Security
The Register - Security
K
Kaspersky official blog
Blog — PlanetScale
Blog — PlanetScale
云风的 BLOG
云风的 BLOG
Hacker News: Ask HN
Hacker News: Ask HN
S
Secure Thoughts
Stack Overflow Blog
Stack Overflow Blog
T
Threat Research - Cisco Blogs
博客园 - 司徒正美
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
F
Fortinet All Blogs
T
Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
小众软件
小众软件
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
博客园 - 聂微东
Attack and Defense Labs
Attack and Defense Labs
B
Blog RSS Feed
Project Zero
Project Zero
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
博客园 - 【当耐特】
V
V2EX
Help Net Security
Help Net Security
P
Proofpoint News Feed
A
Arctic Wolf

Tony Bai

重磅!Tokio官方发布全栈框架Topcoat:不用WASM,AI时代Rust也能“糊”网页了 软件工厂的明与暗:当代码可以自动生产,人类为何必须留下一盏灯? Twitter之父再出手:Block开源Buzz,要让人类和AI Agent「同工同权」 Go 密码学维护者放大招:把 Passkey 存成一行字符串,还顺手为 Go 1.28 写好了 API Loop Engineering才火两个月,硅谷已经卷出“Graph Engineering”了 我开源了 cc-session-migrate :让 Claude Code 会话在多台机器之间自由迁移 从掌上设备的失败到AI时代的基石:Java官方纪录片,揭开一门语言30年的生死赌局 “皇帝的新衣”一年后:对话Thorsten Ball谈Agentic编程 告别标签页焦虑:我让 AI 帮我做了个浏览器插件 TabQueue Bun刚把Zig重写成Rust,这个团队却用487天反向重写 为了一个函数名,Go官方吵了两个月:maps.Same提案近日正式通过 Go 1.28 路线图首度曝光:Cgo 告别C工具链?泛型容器将入标准库? 掌控外环:为什么“循环工程”的边界必须由人类死守? 171个“已批准”却迟迟未实现的提案:Go语言的十年“欠账清单” 别再往 Go 里塞 Java 了:拆解 spf13 的 Idiomatic Go 信仰 AI 不在乎代码烂不烂,但你的Token账单在乎:一项660次实验揭示的编程新常识 10倍速 TypeScript 7.0 正式发布,前Go产品经理:Go才是AI智能体时代的“天选语言” Bun 重写为 Rust 后,Zig 之父罕见开炮:“我们早就等着看你重写” 全新 AI 技术栈:模型、Harness、Loop 与自我进化的智能体 AI 重写 Bun 为Rust全过程揭秘:101万行代码、11天、64个Claude并行开工 MCP Server 架构模式全解析:5 种模式、4 个反模式,与那条不能越过的“工具数量红线” 从手动 govanityurls + Nginx 迁移到 gvu:一次真实迁移记录 Go 私有模块拉取全解:凭据配置 + Vanity URL,个人与组织全覆盖 从“切歌小工具”到“零人工代码”:Claude Code 的诞生史,比科幻还科幻 如何使用 Claude Code 构建 AI 循环系统(Loops) 5 分钟上手 gvu:把 vanity import path 这件事,从“半天运维”变成一条命令 Go 对语言演化的保守态度,在未来 5 年是否仍然正确呢? 五年,三篇文章,一个我一直没真正解决的问题 别把“容易”当“简单”:Gin 框架作者撰文揭秘 88k Star 背后的架构哲学 每个 AI 工程师都应该知道的 20 个循环设计模式 cc-switch-cli:专为终端控与远程开发打造的 Claude Code 多模型切换工具! Andrej Karpathy 解析 Loop Engineering:构建“数日级”长程 Agent 的 9 条黄金法则 HashiCorp 创始人:AI 时代,我们为什么越来越需要有“品味”的程序员? HashiCorp 创始人:AI 时代,我们为什么越来越需要有“品味”的程序员? 一个 Rust 项目吃掉 75GB 硬盘?聊聊 Go 与 Rust 的“缓存焦虑”与拯救指南 折腾过各种语言后,我为什么总是回到 Go 语言? YC 揭秘 AI 原生组织:打造一家在睡梦中自我进化的公司 偿还十年技术债:深度拆解 Go 1.27 的 GODEBUG 强力清理计划 浏览器里的“安全阴谋”:为什么 Go 1.27 的 UUIDv7 会离奇丧失随机性? Go 1.27新特性前瞻:泛型方法落地,标准库内建 UUID - Tony Bai AI 正在撕裂研发团队:狂欢的“托管派”与心碎的“守夜人” - Tony Bai 屠榜 CNCF!为什么在云原生时代,Go 语言能把 Java、C++ 和 Rust 堵在门外? 上千程序员自爆 AI 的“卧槽时刻”:是推开神界大门,还是跌入黑盒地狱? - Tony Bai 大模型正在见顶!传奇架构师:欢迎来到“平坦曲线时代” - Tony Bai Anthropic 40万大样本揭秘:AI 时代为什么“专家”身价暴涨? - Tony Bai 在 AI 编码时代,为什么我们依然选择 Go 而不是 Rust? DeepMind 亮出王炸:别再手写 Agent Harness 了,AI 已经学会自己写了! 为什么说“编译通过,就能运行”?Google 专家 Alice 揭秘 Rust 的工程美学与底层逻辑 谷歌 SRE 重磅白皮书:当 AI 自动写出 10 倍代码,谁来阻止系统崩溃? 别再省 Token 了!硅谷新共识:浪费算力才是唯一捷径 - Tony Bai Linux 内核顶级维护者:写了 35 年 C,是 Rust 让我重新找回了编程的乐趣 拒领上亿、封杀 AI:Zig 之父为什么 10 年不发 1.0? 写地道的 Go 语言,是否能让你成为了一个更好的开发者? - Tony Bai RSA 将死?Let’s Encrypt 押注 MTCs 迎战后量子时代 C++ 的权力游戏:一部关于妥协、背叛与重生的“史诗神剧” - Tony Bai 终结十年纠结:Go 新提案允许 Example 支持任意函数签名 - Tony Bai 2026年,大厂重构核心系统为何集体投向 Go? - Tony Bai “辛辛苦苦考上985,却发现AI能替代我90%的工作”:今天的高考,我们还在为什么而战? - Tony Bai 传奇黑客 Geohot 炮轰 AI Agent:这是软件工程史上代价最昂贵的灾难! 别把 Go 写成 Java:毁掉项目从过度架构开始 - Tony Bai 开源维护者的困境 - Tony Bai AI 时代如何真正掌握一门新技术?这份非主流学习指南建议永久收藏 - Tony Bai Go 生态17年大浪淘沙:2026年最值得引入的10个“神仙级”QoL工具包 - Tony Bai 再见样板代码!Go 官方新提案:函数一键转接口 - Tony Bai 写代码快 10 倍,不等于研发快 10 倍!Google 揭秘 AI 系统级瓶颈 Google I/O 2026:Jeff Dean 携 DeepMind 众神宣告,AI Agent 正在终结“标准化软件”时代 AI 优化 1.5ms,手写 0.02ms!Ghostty 作者痛批 AI 编程“平庸陷阱” Redis 之父吐槽现代前端的复杂性:我们到底是在解决问题,还是在制造问题? - Tony Bai 无痛消灭技术债:Google I/O 2026 开启 Go 自动重构时代 省下 10% CPU!Uber 揭秘 Go 栈扩容的隐秘代价 从 Go 迁移到 Rust - Tony Bai 悄悄用 Go 重写 AI 基础设施:NVIDIA 的 GPU 云平台为何选择 Go? Shopify 23,000 名工程师背后的 Claude Code 配置方案(你可以直接复刻的完整配置) Google 开源 AX 与 Agent Substrate:构建以 Agent 为核心的云原生计算底座 十年难题终获突破:揭秘 Go 1.27 接口逃逸分析优化 - Tony Bai
从 WordPress 到 Hugo:一个 20 年技术博客的迁移实录
Tony Bai · 2026-06-27 · via Tony Bai

题图

本文永久链接https://tonybai.com/2026/06/27/wp-to-hugo-migration-journey

大家好,我是Tony Bai。

2026 年 6 月,我做了一件"蓄谋已久"的事——在AI 的帮助下,将运行了 15 年的 WordPress 博客迁移到了 Hugo 静态站点生成器。这不是一个心血来潮的决定,而是一个技术人对自己"数字花园"的一次深思熟虑的重构。

今天这篇文章,我想聊聊这次迁移的规划方法论、执行过程,以及那些在迁移过程中遇到的坑和解法。如果你也在考虑迁移博客,或者对"如何用 AI 智能体辅助完成一个复杂工程任务"感兴趣,这篇文章应该能给你一些启发。

在聊迁移之前,先简单回顾一下我的博客"进化史"。

BlogBus 时代(2004-2011)

2004 年,我在 BlogBus(博客大巴)上开设了我的第一个技术博客。那个年代,博客是技术人分享知识的主要阵地,BlogBus 是国内为数不多的支持技术内容的托管平台。写博客不需要关心服务器、不需要懂运维,注册账号就能开始写。

然而,托管平台的命运终究不在自己手中。2011 年前后,BlogBus 逐渐式微,平台的不确定性让我决定:是时候拥有一个完全属于自己的博客了

自建 WordPress 时代(2011-2026)

2011 年,我在一台 VPS 上搭建了自己的 WordPress 博客。选择 WordPress 的原因很简单:它是当时最成熟的博客系统,生态丰富,插件众多,开箱即用。

在这 15 年间,WordPress 忠实地记录了我的技术成长——从 C/C++ 到 Go 语言,从传统软件开发到云原生架构,累计近 2,000 篇文章、几千条评论、数千张图片,数据量约 2.6 GB

期间值得一提的里程碑是 HTTPS 的启用。借助 Let’s Encrypt 提供的免费自动化证书,我的博客很早就实现了全站 HTTPS 访问,这在当年还算是一件比较"前卫"的事。

为什么要在 2026 年迁移?

既然 WordPress 跑了这么多年,为什么还要折腾迁移?原因有几个:

1. 性能与安全焦虑

WordPress 是一个动态系统,每次页面请求都需要 PHP 执行 + MySQL 查询。对于一个以"阅读"为主的技术博客来说,这种动态渲染是多余的。更关键的是,WordPress 是黑客最喜欢的攻击目标——即使你持续地更新版本和插件,安全漏洞的风险始终存在。我的WP长期停留在3.2.1版本,漏洞和被攻破的情况,时常发生。

2. 维护成本

PHP 版本升级、插件兼容性、数据库优化、缓存配置……运维 WordPress 就像养一盆需要定期浇水的植物。而静态站点几乎是"免维护"的——构建一次,到处运行。

3. 写作体验

Markdown 已经成为技术人写作的事实标准。在 WordPress 中写 Markdown 需要插件支持,体验总是隔靴搔痒。而 Hugo 天然拥抱 Markdown,配合 Git 版本管理,写作流程可以做到本地写、Git 推、自动部署

4. 掌控感

这一点或许是最重要的。作为一个技术人,我对自己的数字内容有一种"洁癖式"的掌控欲——我希望每一篇文章都是一个纯文本文件,存在 Git 仓库里,不依赖任何数据库,不依赖任何运行时。即使有一天 Hugo 消失了,我的文章依然在那里,用任何文本编辑器都能打开。

迁移方法论:先规划,后执行

面对一个包含近 2,000 篇文章、数千条评论、近3个G图片的复杂迁移任务,我没有选择"边做边想",而是采取了先规划、后执行的策略。

与 Claude Code 的协作模式

这次迁移,我全程使用了 Claude Code(Anthropic 的 AI 编程助手)作为协作工具。整个协作模式是这样的:

第一步:描述目标

我告诉 Claude Code 我的迁移目标:

将 tonybai.com 从 WordPress 迁移到 Hugo(PaperMod 主题),保留所有文章、评论、图片,URL 兼容,部署到一台新的 VPS。

第二步:让 AI 生成迁移计划

Claude Code 分析了我的项目结构、数据量和需求后,生成了一份迁移任务清单——8 个 Phase,40 个 Task,每个 Task 都有明确的输入、输出和验收标准。

第三步:逐 Phase 执行

每个 Phase 开始前,我会让 Claude Code 生成该 Phase 的详细步骤文档(phaseN-steps.md),包括具体的命令、代码和注意事项。然后我们逐步执行,遇到问题时 Claude Code 会分析日志、定位原因、提供修复方案。

这种模式的好处在于:AI 负责规划和细节记忆,人负责决策和验收。我不需要记住 Waline 的 SQLite 表结构,也不需要记住 Caddy 的反向代理配置语法——这些"知识密集型"的部分交给 AI,我只需要关注"这是否符合我的预期"。

8 个 Phase 的全景图

以下是 Claude Code 为我规划的 8 个迁移阶段:

Phase 1: 数据备份与准备          ← 导出 WordPress 数据
Phase 2: Hugo 站点初始化         ← 搭建 Hugo 骨架 + PaperMod 主题
Phase 3: 内容格式转换工具开发     ← 编写 Go 程序批量转换文章
Phase 4: 资源迁移               ← 图片文件迁移与路径替换
Phase 5: 本地构建与验证          ← 构建站点 + URL 兼容性验证
Phase 6: Waline 评论系统部署     ← 替代 WordPress 评论
Phase 7: VPS 部署               ← Caddy + Hugo + DNS 切换
Phase 8: 上线后收尾              ← 写作流程、模板、统计、社交图标等

每个 Phase 之间有明确的依赖关系(如下面Claude Code生成的依赖图)。比如 Phase 3 的内容转换依赖 Phase 1 的 WXR 导出,Phase 7 的部署依赖 Phase 5 的本地验证通过。这种流水线式的执行方式,让整个过程有条不紊。

Phase 1(数据准备)
  T-1.1 ──→ T-1.4
  T-1.2 ──→ (备用)
  T-1.3 ──→ T-3.1
  T-1.5 ──→ T-4.2

Phase 2(Hugo 站点初始化)        Phase 3(内容转换)
  T-2.1 → T-2.2 → T-2.3            T-3.1 → T-3.2 → T-3.3
                 ├→ T-2.4                      │
                 ├→ T-2.5                      ↓
                 └→ T-2.6                   T-4.1 → T-4.2 → T-4.3
Phase 5(本地验证)                              │
  T-3.3 + T-4.2 → T-5.1 → T-5.2 ←────────────┘
                          ├→ T-5.3
                          ├→ T-5.4
                          └→ T-5.5

Phase 6(Waline 评论)
  T-6.1 → T-6.2 → T-6.3
              └→ T-6.4 → T-6.5 → T-6.6
  T-1.4 ──────→ T-6.4

Phase 7(VPS 部署上线)
  T-7.1 + T-7.2 → T-7.3 → T-7.4 → T-7.5 → T-7.6 → T-7.7
  T-6.3 ──────────→ T-7.3
  T-5.1 ─────────────────→ T-7.4

Phase 8(收尾)
  T-7.6 → T-8.1, T-8.2
  T-6.3 → T-8.3
  T-7.5 → T-8.4
  T-6.5 → T-8.5

Phase 1-2:数据备份与站点初始化

这两个阶段相对标准化。

Phase 1 主要做了三件事:

  1. 通过 mysqldump 备份 WordPress 数据库
  2. 通过 WordPress 的 WXR 导出功能,导出全量文章和评论数据
  3. 打包 wp-content/uploads 目录(近5000 张图片,2.6 GB)

Phase 2 搭建 Hugo 骨架:

hugo new site tonybai-blog
cd tonybai-blog
git submodule add https://github.com/adityatelange/hugo-PaperMod themes/hugo-PaperMod

选择 PaperMod 主题的原因是:它对中文(CJK)支持良好、设计简洁、维护活跃、性能优秀,也是hugo生态排行第一的Theme。

在这个阶段,有一个关键配置决策——permalink 结构:

permalinks:
  posts: "/:year/:month/:day/:slug/"

这个配置确保 Hugo 生成的 URL 路径与 WordPress 完全一致(/2026/06/26/my-article/),从而保证所有外部链接不会失效。这是整个迁移中最重要的决策之一。

Phase 3:内容转换——用 Go 写一个批量转换器

这是整个迁移中技术含量最高的阶段。近2000 篇文章需要从 WordPress 的 HTML 格式转换为 Hugo 的 Markdown + YAML Front Matter 格式。

我没有选择手动转换(显然不现实),也没有使用现成的转换工具(灵活性不够),而是让 Claude Code 帮我用 Go 编写了一个专用的转换器

转换器的核心逻辑

WXR (XML) → 解析每篇文章 → 提取标题/日期/slug/标签/内容
          → 生成 YAML Front Matter
          → 将 HTML 内容转为 Markdown
          → 替换图片路径
          → 写入 .md 文件

遇到的坑

坑 1:标题格式不统一

我的文章标题有两种格式——早期是 Title: 文章标题 | Tony Bai,后期变成了 Title: 文章标题 - Tony Bai。转换器需要同时处理这两种分隔符:

// 处理两种标题格式: "| Tony Bai" 和 "- Tony Bai"
re := regexp.MustCompile(`\s*[\|]\s*Tony\s*Bai\s*$|\s*-\s*Tony\s*Bai\s*$`)
title = re.ReplaceAllString(rawTitle, "")

坑 2:2025 年后的文章"尾巴"问题

2025 年后的文章导出内容中,混入了数百行的网页模板噪音——评论表单、侧边栏、月度归档列表等。这些内容出现在正文之后,如果不清理,会全部混入 Markdown 文件。

解决方案是在版权标记处截断:

// 在版权标记处截断,清除后续模板噪音
cutoffRe := regexp.MustCompile(`\n© \d{4},`)
if loc := cutoffRe.FindStringIndex(content); loc != nil {
    content = content[:loc[0]]
}

坑 3:图片路径批量替换

WordPress 中所有图片的引用格式是 https://tonybai.com/wp-content/uploads/...,需要统一替换为 Hugo 的静态路径 /images/wp-content/uploads/...。这个替换在转换器中自动完成。

Phase 4-5:资源迁移与本地验证

Phase 4 处理图片迁移。近3个G的图片需要从 VPS 迁移到 Hugo 项目的 static/images/wp-content/uploads/ 目录下。

Phase 5 是关键的验证阶段。我用 Go 编写了一个 verify-urls 工具,自动对比 WordPress sitemap 中的 URL 与 Hugo 构建输出的 HTML 文件——最终验证通过率为 99.3%。剩余 0.7% 的差异主要是标签页和分类页的路径不同(Hugo 使用 /tags/xxx/ 而非 /tag/xxx/),可以通过重定向规则解决。

Phase 6:Waline 评论系统——最大的"坑"

评论系统的迁移是整个过程中最曲折的部分。

我选择了 Waline 作为评论系统——它是一个轻量级的自托管评论系统,后端使用 Node.js,数据库支持 SQLite,非常适合个人博客。

Waline 的 Docker 部署

services:
  waline:
    image: lizheming/waline:latest
    container_name: waline
    restart: always
    network_mode: host
    volumes:
      - ./data:/app/data
    environment:
      - SITE_NAME=Tony Bai
      - SITE_URL=https://tonybai.com
      - AUTHOR_EMAIL=xxx@xxx.com
      - JWT_TOKEN=xxxxx
      - SQLITE_PATH=/app/data

踩坑记录

坑 1(致命级):Waline Docker 不会自动创建 SQLite 表

这是我遇到的最大的坑。启动 Waline 容器后,SQLite 数据库文件被创建了,但打开一看——是 0 字节。没有表结构,什么都没有。

翻阅 Waline 源码后发现:Docker 镜像中的 SQLite 适配器只负责"连接"数据库,不负责"初始化"表结构。这意味着首次部署时必须手动创建表

我从 Waline 的 GitHub 仓库找到了官方 schema,手动执行了建表语句:

CREATE TABLE "wl_Users" (...);
CREATE TABLE "wl_Comment" (...);
CREATE TABLE "wl_Counter" (...);

坑 2:Docker 端口映射不生效

默认情况下,Docker 容器内的 Waline 绑定到 127.0.0.1(而非 0.0.0.0),导致端口映射 (ports: "8360:8360") 无法工作。

解决方案是使用 network_mode: host,让容器直接使用宿主机的网络栈。

坑 3:SQLite 数据库锁

导入评论数据时,如果 Waline 容器正在运行,会报 database is locked 错误。需要先停止容器,导入数据,再重启:

docker compose stop
./import-comments -input comments.json -db /opt/waline/data/waline.sqlite
docker compose start

坑 4:评论父子关系映射

WordPress 的评论有嵌套回复,使用 comment_parent 字段关联。导入 Waline 后,由于 Waline 使用自增 ID,原来的 WordPress 评论 ID 不再有效。

我的导入工具在导入过程中维护了一个 wpIDToRowID 映射表,将 WordPress 评论 ID 映射到 Waline 的新行 ID,确保嵌套关系正确。

评论导入结果

最终成功导入了全部存量评论内容,所有父子关系完整保留。

Phase 7:VPS 部署与 DNS 切换

Phase 7 是将 Hugo 站点部署到 VPS 的最后冲刺。

Caddy 生产配置

我使用 Caddy 作为 Web 服务器,生产环境的 Caddyfile 核心配置如下:

tonybai.com {
    root * /var/www/tonybai-blog/public

    # Waline 评论反向代理
    handle /waline/* {
        uri strip_prefix /waline
        reverse_proxy localhost:8360
    }

    # WordPress /feed/ 兼容重定向
    redir /feed/ /index.xml 301

    # 静态文件服务
    handle {
        file_server
    }

    # 静态资源长缓存
    @static path *.css *.js *.png *.jpg *.jpeg *.gif *.svg *.webp *.woff *.woff2
    header @static Cache-Control "public, max-age=31536000, immutable"

    # HTML 不缓存
    @html path *.html /
    header @html Cache-Control "public, max-age=0, must-revalidate"
}

几个值得注意的设计:

  • uri strip_prefix /waline:Waline 部署在 /waline 子路径下,Caddy 在转发前剥离这个前缀,让 Waline 以为自己运行在根路径
  • /feed//index.xml 重定向:WordPress 的 RSS 订阅地址是 /feed/,Hugo 的是 /index.xml,301 重定向确保老订阅者不会丢失
  • 分级缓存策略:静态资源(图片、CSS、JS)缓存 1 年(immutable),HTML 不缓存(确保更新即时生效)

DNS 切换前的验证

在 DNS 切换之前,我用 curl --resolve 模拟域名请求来验证配置:

curl -sk --resolve tonybai.com:443:127.0.0.1 "https://tonybai.com/" | head -20

确认一切正常后,将 tonybai.com 的 A 记录从旧 VPS IP 指向新 VPS IP。由于使用了 Cloudflare 代理,DNS 传播几乎是即时的。

Phase 8:上线后的收尾工作

DNS 切换完成后,还有一些收尾工作:

  • 文章模板(Archetype):创建了 archetypes/posts.md,预设 Front Matter 字段,hugo new 一篇新文章时自动填充模板
  • 访问统计:通过 PaperMod 的 extend_head.html 扩展点集成 StatCounter
  • 社交图标:PaperMod 内置了 100+ 社交图标 SVG,只需在 hugo.yml 中配置即可
  • Waline 管理:配置 SMTP 邮件通知、设置定时备份脚本(Git 提交到独立仓库)
  • Sitemap 提交:向 Google Search Console、Bing Webmaster、百度站长平台提交 sitemap

图片管理策略

2.6 GB 的图片数据不适合放在 Git 仓库中。我评估了四种方案:

方案 优点 缺点
A. 单仓库 简单 仓库过大,Git 操作缓慢
B. Git LFS 透明 GitHub 免费额度不足
C. 独立图片仓库 职责分离 需要维护两个仓库
D. rsync + Cloudflare R2 不依赖 Git 需要配置同步工具

最终我选择了方案 D:本地电脑作为图片来源,通过 rsync 同步到 VPS,同时用 rclone 定期备份到 Cloudflare R2(免费 10 GB 存储,无出口流量费)。三份副本,安心。

迁移成果一览

指标 数据
文章数量 1,955 篇
评论数量 2,219 条(622 篇文章)
图片数量 4,667 张(2.6 GB)
URL 兼容率 99.3%
构建时间 ~15 秒(Hugo --minify
页面加载 纯静态,毫秒级响应
迁移耗时 约 2 天

几点经验总结

1. 规划比执行更重要

面对复杂任务,花 20% 的时间做规划,能节省 80% 的返工时间。让 AI 生成迁移计划,然后人工审核和调整,是目前最高效的协作模式。

2. 自动化验证是必须的

近2000 个 URL 的兼容性验证,人工检查是不现实的。用 Go 编写一个自动验证工具,不仅能节省时间,还能在后续修改时反复运行。

3. URL 兼容性是第一优先级

对于一个运营了 20 年的博客,外部链接(搜索引擎、社交媒体、其他博客的引用)是宝贵的"数字资产"。permalink 结构保持一致,是整个迁移中最重要的决策。

4. 评论迁移是最容易踩坑的部分

评论系统涉及数据库 schema、数据格式转换、嵌套关系映射等多个技术点。Waline 的 SQLite 不自动建表这个问题,如果没有 AI 帮忙分析源码,可能会耗费大量排查时间。

5. AI 辅助编程的真正价值

这次迁移中,Claude Code 的最大价值不是"帮我写代码",而是:

  • 知识检索:Waline 的表结构、Caddy 的反向代理语法、PaperMod 的扩展点——这些"用完即忘"的知识,AI 信手拈来
  • 规划能力:将模糊的需求拆解为可执行的步骤
  • 问题诊断:遇到错误时,AI 能快速分析日志、定位原因、提供修复方案

小结

从 2004 年在 BlogBus 上写下第一篇技术文章,到 2026 年将博客迁移到 Hugo,这 20 年的博客历程,也是互联网技术变迁的一个缩影。

托管平台 → 自建 WordPress → 静态站点,这条路径的背后,是一个技术人对内容自主权技术掌控感的持续追求。

Hugo 静态站点不是终点,但它可能是目前最适合个人技术博客的形态:快速、安全、可控、低成本。更重要的是,它让我重新找回了"写博客"的纯粹感——打开编辑器,写 Markdown,Git push,完事。

如果你也有一个"年久失修"的 WordPress 博客,现在是时候考虑迁移了。借助 AI 辅助编程工具,这个过程远比你想象的简单。

注:迁移后的博客可能依然有“未发现”的小问题,如果大家发现了,还烦请留言告知🙏。


✍️ 今日开放讨论:

  1. 你的博客目前使用什么系统?有没有考虑过迁移到静态站点?
  2. 在博客迁移过程中,你最担心的是什么?
  3. 你使用 AI 辅助编程完成过什么复杂的工程任务?

欢迎在评论区分享你的经验。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!

我们致力于打造一个高品质的 Go 语言深度学习AI 应用探索 平台。在这里,你将获得:

  • 体系化 Go 核心进阶内容: 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏,夯实你的 Go 内功。
  • 前沿 Go+AI 实战赋能: 紧跟时代步伐,学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等,掌握 AI 时代新技能。
  • 星主 Tony Bai 亲自答疑: 遇到难题?星主第一时间为你深度解析,扫清学习障碍。
  • 高活跃 Gopher 交流圈: 与众多优秀 Gopher 分享心得、讨论技术,碰撞思想火花。
  • 独家资源与内容首发: 技术文章、课程更新、精选资源,第一时间触达。

衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

img{512x368}


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。