博客迁上 Cloudflare Workers 全记录:D1 + R2 + Hono

把跑在树莓派上的博客整体迁移到 Cloudflare Workers:单 Worker 架构、Express 到 Hono、bcrypt 到 PBKDF2,以及一路踩过的坑。

这个博客从今天起不再跑在我家的树莓派上了。前后端整体迁到了 Cloudflare Workers,域名也换成了现在你看到的这个。记录一下完整过程。

为什么迁

博客跑在树莓派 + Cloudflare Tunnel 上两个月,能用,但:家里断网就下线、树莓派要兼职跑其他服务、备份全靠自觉。博客这种纯 Web 应用,其实是最适合 Serverless 的负载。

目标架构:一个 Worker 搞定所有

freakz2z.com
   └── Cloudflare Worker (Hono)
        ├── Static Assets → 前端构建产物(SPA)
        ├── /api/*        → 后端路由
        ├── /uploads/*    → R2 存储
        └── D1            → SQLite 数据库

关键决策是不用 Pages + Workers 分离,而是单 Worker 挂 Static Assets。前后端同源,CORS 问题不存在,前端的 baseURL: '/api' 一行不用改。

// wrangler.jsonc
"assets": {
  "directory": "../client/dist",
  "not_found_handling": "single-page-application",
  "run_worker_first": ["/api/*", "/uploads/*", "/sitemap.xml"]
}

runworkerfirst 是精髓:静态资源直出,只有这几类路径进 Worker 逻辑。

Express → Hono

九个路由文件全部手工移植。Hono 的 API 和 Express 神似,大部分是机械劳动:

// Express
router.get('/:id', async (req, res) => res.json(data));
// Hono
app.get('/:id', async (c) => c.json(data));

值得一提的坑:hono/jwt 的 verify 必须显式传算法 verify(token, secret, 'HS256'),不传直接抛 JwtAlgorithmRequired。所有带鉴权的接口集体 401,排查了好一会。

bcrypt → PBKDF2

Workers 免费版单请求 CPU 时间 10ms,bcrypt 一次哈希就要几十上百毫秒,直接超限。换成 WebCrypto 原生支持的 PBKDF2(10 万次迭代),登录接口瞬间恢复正常。

密码哈希格式设计成自描述的:pbkdf2$100000

lt;salt>
lt;hash>
,以后想换参数或算法可以平滑过渡。

文件存储 → R2

multer 磁盘存储换成 R2 put()/uploads/* 路由从 R2 读取并加 7 天缓存头。URL 结构不变,历史文章里的图片链接全部原样可用。

一个本地开发的坑:wrangler CLI 往本地 miniflare R2 上传中文文件名会被百分号编码,线上没这个问题。本地调试图片 404 时别慌,先看看 key 长什么样。

数据迁移

MySQL → D1 的方言部分之前写过一篇,这里只说流程:脚本从 MySQL 导出 JSON → 程序化生成 INSERT → wrangler d1 execute 灌入。迁完逐表 COUNT 对比,再抽了几篇文章做字节级 diff,确认内容零丢失(连访问计数都原样保留了)。

效果

  • 全球边缘节点响应,不再受家庭宽带上行限制
  • 树莓派卸下博客负载,安心跑别的
  • 免费额度(10 万请求/天)对个人博客来说根本用不完

如果你也有个跑在自己设备上的小站,认真考虑一下这条路。