从 WordPress 到 EmDash:我的博客迁移到 Cloudflare 边缘的完整记录

从 WordPress 迁到 Cloudflare 边缘的完整记录:为什么离开 PHP + MySQL 的笨重架构,EmDash + D1 + R2 的纯边缘方案怎么部署,六步可复刻流程,以及真实的代价清单

为什么离开 WordPress

我的博客以前跑在 WordPress 上,一年多下来问题很实在:

  • 要养一台后端服务器:PHP + MySQL,进程、数据库、备份全自己维护。流量再小,服务器也得 7×24 开着
  • 掉线:服务器重启、内存泄漏、MySQL 连接数被打满,用户访问 502,每次都要 SSH 上去救
  • 安全漏洞多:WordPress 生态 96% 的安全漏洞来自插件。插件和主题直接跑在站点进程里,有完整的数据库和文件系统权限——装一个插件等于把整个站点交给它。WordPress.org 插件审核队列常年 800+ 个排队,漏洞修复永远追不上
  • 升级痛苦:PHP 版本、插件兼容性、主题冲突,每次大版本升级都像拆弹

这套架构是 2003 年的设计:租一台服务器,跑一个长驻进程,接一个 SQL 数据库,一遍遍渲染 HTML。二十多年了,托管方式早就变了,CMS 也应该变。

为什么选 EmDash

Cloudflare 在 2026 年 4 月开源了 EmDash,定位是"WordPress 的精神继承者"。MIT 协议,全 TypeScript,跑在 Cloudflare Workers 上:

  • 纯边缘:没有服务器。代码部署到 Cloudflare 全球网络,按 CPU 时间计费,没流量时缩到零,闲置零成本
  • 数据层 D1 + R2:D1 是边缘 SQLite,R2 是对象存储(无出口流量费)。底层用 Kysely/S3 抽象,理论上也能换 PostgreSQL 或自托管 Node
  • 插件沙箱化:每个插件跑在独立的 Worker isolate 里,只能使用 manifest 里显式声明的能力(类似 OAuth scope)。装插件前就知道它要什么权限——这是对 WordPress 插件安全模型最根本的修正
  • AI 原生:内置 MCP server 和 CLI,agent 可以直接管理内容
  • Astro 主题:主题就是一个 Astro 项目,前端开发者零学习成本

部署记录

前置条件

Node.js 18+(我用 22)
一个 Cloudflare 账号
wrangler 认证:CLOUDFLARE_API_TOKEN 环境变量

1. 脚手架

npx create-emdash@latest . --template cloudflare:blog --yes --force

生成 Blog Starter 模板:2 个 content collection、7 篇示例文章、全文搜索、RSS、明暗模式、表单插件、webhook 通知插件。

2. 安装依赖

模板锁的 @cloudflare/workers-types@^4 和最新 wrangler 要求的 ^5 有 peer 依赖冲突,需要 legacy 模式:

npm install --legacy-peer-deps

国内网络如果 npm 直连慢,可先 npm config set registry https://registry.npmmirror.com。

3. 配置 wrangler.jsonc

{
  "name": "laobaiblog",                      // 改成你的 Worker 名
  "main": "./src/worker.ts",
  "account_id": "<YOUR_ACCOUNT_ID>",
  "compatibility_date": "2026-02-24",
  "compatibility_flags": ["nodejs_compat"],
  "d1_databases": [{ "binding": "DB", "database_name": "laobaiblog-db" }],
  "r2_buckets": [{ "binding": "MEDIA", "bucket_name": "laobaiblog-media" }],
  "worker_loaders": [{ "binding": "LOADER" }],   // 插件沙箱,免费版需注释掉
  "triggers": { "crons": ["* * * * *"] },        // 维护 cron
  "routes": [{ "pattern": "blog.laobaitv.net/*", "zone_name": "laobaitv.net" }]
}

注意:Dynamic Workers(插件沙箱)目前仅付费/企业账号可用,免费账号注释掉 worker_loaders 即可,其他功能不受影响。

4. 创建数据库和存储

npx wrangler d1 create laobaiblog-db     # 返回 database_id,写回 wrangler.jsonc
npx wrangler r2 bucket create laobaiblog-media

5. 构建 + 部署

npx astro build
npx wrangler deploy

一次 deploy 自动完成:上传 Worker、绑定 D1/R2、自动创建 KV 会话空间(laobaiblog-session)、绑定 Cloudflare Images 和 Worker Loader、注册路由和 cron。我的部署输出:

Uploaded laobaiblog (90.82 sec)
Deployed laobaiblog triggers (2.11 sec)
  blog.laobaitv.net/* (zone name: laobaitv.net)
  schedule: * * * * *

6. 首次初始化

部署后访问域名,302 到 /admin/setup 引导页,三步完成:

  1. Site:站点标题、副标题、是否导入示例内容
  2. Account:管理员邮箱、姓名
  3. Sign In:创建 Passkey(WebAuthn 设备认证,无密码)

Setup 向导自动执行数据库迁移并导入种子内容,完成后即可登录后台。

部署前后对比

维度

WordPress

EmDash

运行位置

VPS / 虚拟主机

Cloudflare 全球边缘

运行时

PHP + MySQL

TypeScript + D1 (SQLite)

服务器维护

自己管

无

流量峰值

提前扩容或排队

自动伸缩

闲置成本

服务器照付

约零(scale-to-zero)

插件权限

全库 + 文件系统

沙箱 + 显式能力声明

内容格式

HTML 富文本

Portable Text(结构化 JSON)

更新方式

后台更新 + 备份 + 兼容性测试

git push 自动部署

现在的架构

浏览器 → Cloudflare 边缘 → Worker (Astro SSR)
                         ├─ D1 (内容、评论、全文搜索 FTS5)
                         ├─ R2 (图片媒体)
                         ├─ KV (会话)
                         └─ Worker Loader (插件沙箱)

写文章:后台编辑器(TipTap)→ 存 D1 → 实时生效,全程不需要重新部署。搜索、RSS、分类、标签、明暗模式模板自带。

已知代价(如实记录)

  • 生态还是零:对比 WordPress 六万+ 插件,EmDash 还在 beta,需要的功能大概率要自己写插件
  • 绑定 Cloudflare:虽然宣称可移植,但最佳体验在 CF 全家桶里
  • beta 期:API 会变,生产环境要跟着上游走
  • 内容思维要换:结构化 JSON 比 HTML 富文本更强大,但迁移过来要适应

对单人博客来说,换掉的是一台 24 小时开机、会掉线、要打补丁的服务器,换来的是一个全球分布、能缩到零、内容实时生效的站点。这笔账是划算的。

No comments yet