搬家到 Cloudflare
这次把 61DH 的 VuePress 静态网站从自己的服务器迁到 Cloudflare,顺手记录部署步骤和踩过的坑,方便以后参考。
1. 这个网站需要什么运行环境?
文章用 Markdown 写,VuePress 在构建时生成 HTML、CSS 和 JavaScript:
Markdown → VuePress build → docs/.vuepress/dist → 静态托管
Node.js 是构建时需要的工具,访问生成后的文章不需要运行 Node.js 服务。
原来的 deploy.sh 先执行 php genIndex.php 生成英语文章索引,再执行 npm run build,最后通过 scp 上传生成文件到服务器。现在改用 Cloudflare 从 Git 仓库自动构建和部署。
2. 我这次用的是 Workers
Cloudflare Pages 可以托管静态网站,Workers 也可以通过 Static Assets 托管。这次进入的创建页面有 Build command 和 Deploy command,使用的是 Workers 的部署流程。
连接 GitHub 仓库 deniapps/61dh,配置如下:
| 项目 | 设置 |
|---|---|
| Project name | 61dh |
| 项目根目录 | 仓库根目录 |
| Build command | npm run build |
| Deploy command | npx wrangler deploy --assets ./docs/.vuepress/dist |
| Preview builds | 初次部署先关闭 |
Cloudflare 会安装依赖,然后执行构建和部署。这里不用运行原来的 deploy.sh。
3. 添加 Wrangler 配置
仓库根目录需要提交 wrangler.jsonc:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "61dh",
"compatibility_date": "2026-11-10",
"assets": {
"directory": "./docs/.vuepress/dist"
}
}
name 与 Cloudflare 上的 Worker 名称一致,assets.directory 指向 VuePress 的构建结果。配置了目录后,部署命令也可以简化为 npx wrangler deploy。
compatibility_date 用来确定 Workers 的兼容行为,不是文章日期,也不用每次发布都修改。第一次部署缺少这个字段,构建虽然通过了,上传时仍然报错:
A compatibility_date is required when uploading a Worker.
所以要同时确认 Build command completed 和部署成功,不能只看 VuePress build 成功。
4. 图片在本地,为什么 Cloudflare 找不到?
第一次构建报错:
Rollup failed to resolve import "/uploads/2025-09-09-17-07-12.png"
原因是 .gitignore 原来有一行 uploads,把 docs/.vuepress/public/uploads 也忽略了。本地有图片,但 Cloudflare 克隆仓库后没有这些文件。
保留原来的忽略规则,并给网站公共图片目录加例外:
uploads
!docs/.vuepress/public/uploads/
!docs/.vuepress/public/uploads/**
然后把图片一起提交:
git add .gitignore docs/.vuepress/public/uploads
git commit -m "Include public uploads for Cloudflare builds"
git push
这类错误要先检查资源有没有进入 Git,不能只确认本地路径存在。
5. 绑定 61dh.com
部署成功后,先打开 Worker 提供的 workers.dev 地址,检查首页、文章和图片,再切换正式域名。
在 Cloudflare 控制台中:
- 打开 Workers & Pages → 61dh。
- 进入 Settings → Domains & Routes。
- 点击 Add → Custom Domain。
- 输入
61dh.com,不加https://,也不加路径。 - 确认后等待域名和 HTTPS 证书生效。
前提是 61dh.com 已经作为有效的域名区域加入同一个 Cloudflare 账户。我选择统一使用不带 www 的地址,www.61dh.com 通过下面的规则跳转到根域名。
已有 DNS 记录的冲突
添加时遇到了:
Hostname '61dh.com' already has externally managed DNS records
(A, CNAME, etc). Delete them first or try a different hostname.
这里的 externally managed 表示已有记录不是由这个 Worker 的 Custom Domain 管理,不一定表示 DNS 在 Cloudflare 之外。
之前使用 Pages 时,设置流程处理了 DNS 更新;这次 Workers 的 Custom Domain 流程拒绝覆盖现有记录,需要先手动处理冲突。
操作步骤:
- 进入 61dh.com → DNS → Records。
- 先记录指向旧服务器的根域名记录类型、值和代理状态,方便必要时恢复。
- 删除与
61dh.com冲突的根域名 A、AAAA 或 CNAME 记录;根域名可能显示为@。 - 回到 Worker,再次添加
61dh.com作为 Custom Domain。
Cloudflare 会为 Custom Domain 管理 DNS 和证书。删除旧记录到绑定完成之间可能有短暂中断,所以确认新站能访问后再操作。
只处理这次迁移的主机名。保留 MX、TXT、comments.61dh.com 和其他子域名记录。
把 www 跳转到非 www
最终希望的行为是:
https://www.61dh.com/diary/example.html?a=1
→ https://61dh.com/diary/example.html?a=1
DNS 的 CNAME 只处理域名解析,不会让浏览器自动修改地址。Workers Custom Domain 又是按请求的主机名匹配,所以只配置 www → 61dh.com 的 CNAME,还不能完成跳转。
配置步骤:
- 保持
61dh.com绑定到 Worker61dh。 - 在 DNS → Records 中,把
www配置为指向61dh.com的 CNAME,并开启 Proxied(橙色云朵)。 - 进入 Rules → Redirect Rules → Create rule;如果有 Redirect from WWW to root 模板,可以直接使用。
- 匹配主机名
www.61dh.com,目标使用https://61dh.com并保留原路径,状态码选择 301,开启 Preserve query string。 - 部署规则,访问带路径和查询参数的
www地址,确认浏览器跳转后路径和参数都保留。
如果手动配置规则,匹配表达式可以写成:
(http.host eq "www.61dh.com")
目标 URL 选择 Dynamic,使用表达式:
concat("https://61dh.com", http.request.uri.path)
查询参数由 Preserve query string 单独保留。这个方案不需要把 www.61dh.com 再添加为 Worker Custom Domain;跳转由 Cloudflare 的 Redirect Rule 处理。
6. PHP 和评论服务要单独考虑
php genIndex.php 是构建前的索引生成工具。当前 npm run build 不包含这一步,所以需要在本地运行并提交更新后的 Markdown:
php genIndex.php
如果以后希望完全自动化,可以把索引生成逻辑改成 Node.js,并加入构建流程。另外,该脚本按文件修改时间排序;Git checkout 后的修改时间不一定等于文章发布时间。
网站还有 quiz.html 调用 quiz_api.php。Workers Static Assets 不会执行 PHP,这个功能需要改成静态 JSON、Workers API,或者继续请求单独托管的 PHP 服务。
评论使用的是 Waline:https://comments.61dh.com。迁移文章的静态托管不会迁移 Waline 后端,评论域名继续指向原来的评论服务。
7. 以后如何更新文章
- 新增或修改 Markdown,图片放入
docs/.vuepress/public/uploads/。 - 如果英语文章索引有变化,在本地执行
php genIndex.php。 - 本地执行
npm run build检查。 - 提交文章、图片及生成的索引,推送到 Cloudflare 配置的生产分支。
- 检查 Cloudflare 的构建和部署结果,再打开正式域名确认。
原来的 scp 上传步骤已经注释掉。以后发布主要靠 Git push 触发自动部署。
