为什么离开 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-media5. 构建 + 部署
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 引导页,三步完成:
- Site:站点标题、副标题、是否导入示例内容
- Account:管理员邮箱、姓名
- 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