从 GitHub 到 Cloudflare:把静态站真正上线
按我们实际踩坑的顺序,从域名 NS、GitHub 托管、本地构建到 Workers 部署和线上确认。重点不是命令,是哪一步会白等一晚上。
本页目录
- 这篇解决什么问题
- 0. 我们实际走的链路
- 1. 域名:买在哪、为什么要改 NS、改之前关什么
- 1.1 买域名
- 1.2 为什么要改 DNS 服务器
- 1.3 改 NS 之前,先关掉域名展示页
- 1.4 绑定 apex 和 www
- 2. 代码托管:GitHub 推上去之后 CI 看什么
- 3. 构建:静态站怎么编译、产物在哪、本地怎么验证
- 3.1 本地构建
- 3.2 构建前跑什么
- 3.3 Cloudflare 的 Build command 怎么填
- 4. 部署:Git 推送自动构建 vs 命令行直传
- 4.1 自动:push master
- 4.2 手动:本机 wrangler deploy
- 5. Deployments 显示成功,为什么线上还是旧的
- 5.1 我们遇到的现象
- 5.2 先按这个顺序查
- 5.3 这次怎么解决的
- 5.4 验证时别被代理骗了
- 6. 出问题时:构建卡住、代理、令牌
- 6.1 构建卡住或失败
- 6.2 代理
- 6.3 令牌和权限
- 6.4 回滚
- 7. 上线检查清单
- 8. 哪些是实测,哪些是未实测
- 下一步读什么
30 秒速查
- 先买域名、接 Cloudflare,等 zone Active 再绑域名
- GitHub push 只是早期信号,不等于部署
- Cloudflare Build command 用
npm run verify,构建藏在它最后一步- Deployments Ready 不等于线上更新,必须 curl
- 所有线上 curl 加
--noproxy '*'
这篇解决什么问题
网上讲上线的文章,大多是把官方文档换个说法:点这里、填那里、等一会儿。真到自己做,卡住的地方往往不是“不会点”,而是:
- 域名注册商把 DNS 锁住了,改不了 NS;
- GitHub 绿了,Cloudflare 却没部署;
- Deployments 显示 Ready,线上还是旧页面;
- 本机代理让 curl 超时,误判成站点挂了。
这篇按我们实际走的顺序写。每一步都回答三件事:做什么、为什么、怎么确认成了。没实测过的地方会标出来。
0. 我们实际走的链路
ename 买域名
→ Cloudflare Add a site
→ 关掉域名展示页,改 NS
→ 等 zone Active
→ Worker 绑 apex / www
→ GitHub 推 master
→ Cloudflare Workers Builds:npm run build → npx wrangler deploy
→ curl --noproxy '*' 验证线上
这不是官方分类,是实际顺序。域名和 DNS 在前,是因为后面 Worker 绑自定义域名依赖 zone;代码和构建在后,是因为本地能跑通再谈上线。
1. 域名:买在哪、为什么要改 NS、改之前关什么
1.1 买域名
我们买在 ename。选它没有神秘理由,就是当时能买、能改 NS。价格和续费我没记录,未实测,别把本文当购买建议。
买完先别急着写代码。先确认两件事:注册商后台能不能改 NS,域名有没有被“展示页”之类的功能占用。后面会卡在这。
1.2 为什么要改 DNS 服务器
Cloudflare 要接管 DNS,才有 zone 这个概念。Worker 绑定自定义域名、apex 跳转、缓存和证书,都建立在这个 zone 上。
apex 域名(genwozuo.com)本来就不适合直接 CNAME 到别处;改 NS 让 Cloudflare 成为权威 DNS,是最干净的路。
怎么确认成了:Cloudflare Overview 显示 Active;命令行查 NS:
dig +short NS genwozuo.com @1.1.1.1
我们实测返回:
frida.ns.cloudflare.com.
sri.ns.cloudflare.com.
两条都在,才算 NS 生效。
1.3 改 NS 之前,先关掉域名展示页
这一步最容易白忙。ename 后台如果开着「域名展示页」,点修改 DNS 会直接报:
域名已设置展示页,不能修改DNS
处理顺序是:管理中心 → 我的域名 → 域名 → 更多功能 → 域名展示页 → 关闭。关掉之后,再去 DNS 那一行点「修改DNS」,填 Cloudflare 给的两个 NS。
注意:解析列表里的 NS 行是锁死的,不能直接删改;入口在 DNS 行的「修改DNS」,不是在记录列表里编辑。
怎么确认成了:回到域名管理页,NS 已经变成 Cloudflare 的两个地址;Cloudflare 侧从 Pending 变成 Active。NS 生效不是秒级,别在 Pending 时反复绑域名。
1.4 绑定 apex 和 www
apex 用 Worker 的自定义域名:
Workers & Pages → genwozuo → Domains → Add custom domain → genwozuo.com
Cloudflare 会自动生成一条 Worker 类型的记录,不用手动建。
www 有两条路:
- 首选(我们现在线上):Domains → Add custom domain,填
www.genwozuo.com。 - 如果界面报
No zones match:退回 DNS 记录 + Worker Route。我们早先实测过这条:DNS 加一条A/www/192.0.2.1(RFC 5737 占位地址)/ Proxy 开启;Worker → Domains → Add Route,填www.genwozuo.com/*,Failure mode 保持 Fail closed。
第一条是当前线上在用的;第二条是备选,不是首选。两条都能成,区别是入口和后续维护方式。
apex 到 www 的 301 用 Redirect Rule:
Hostname equals genwozuo.com
→ https://www.genwozuo.com + 原路径
→ 301,保留 query string
怎么确认成了:
curl -sSI --noproxy '*' https://www.genwozuo.com/ | head -1
curl -sI --noproxy '*' https://genwozuo.com/ | grep -i location
第一条应为 HTTP/2 200;第二条应看到 301 到 https://www.genwozuo.com/...。少一个,域名就没完全通。
2. 代码托管:GitHub 推上去之后 CI 看什么
仓库在 GitHub 私有组织 genwozuo 下,Cloudflare 的 Git 连接同一个组织。日常在 dev 上改,发布时快进到 master。
GitHub Actions 的 verify job 跑的是 verify 那几项(verify:fast / verify:tutorials / verify:media / verify:site / verify:ui),这是早期信号,不是部署闸门。GitHub Free 私有仓库不强制要求检查通过才能合并,所以“Actions 绿了”不等于“Cloudflare 会部署”。
CI 红怎么看:先点进 job 日志。Annotations 只写 exit code 1,不是日志;真正的报错在 job 输出里。已知坑是 Node 版本:Astro 7 要求 Node >= 22.23.4,我们 CI 固定用 24。
怎么确认成了:Actions 里最新一次 verify 是绿的,job 日志里能看到类型检查、教程测试、媒体测试、站点检查依次通过。绿了只代表代码过了本地这层,不代表已上线。
3. 构建:静态站怎么编译、产物在哪、本地怎么验证
3.1 本地构建
npm run build
产物在 dist/。我们的站构建后是 70 个页面(这个数字会随文章增加,别把它当固定值)。
构建完别只看退出码。至少确认:
test -f dist/index.html
find dist -name '*.html' | wc -l
第一条通过、第二条数量和预期差不多,才算产物真的在。再本地起一个静态服务看页面:
npm run serve:dist
页面能打开、样式在、关键路由不 404,才算本地构建可用。
3.2 构建前跑什么
本地发布前跑:
npm run verify
它会跑类型检查、教程测试、媒体测试和站点检查。你要的是 exit 0,不是“看起来没报错”。
3.3 Cloudflare 的 Build command 怎么填
我们线上配置:
Build command: npm run verify
Deploy command: npx wrangler deploy
注意构建是藏在 verify 里面的:本项目的 verify 最后一步是 verify:site,它会先跑 npm run build 生成 dist/,再校验站点。所以 Build command 只填 npm run verify,不要再拼一次 npm run build。
换项目时先确认你的 verify 里有没有构建这一步:有,就填 npm run verify;没有,才填 npm run verify && npm run build。以你项目的 package.json 为准,别照抄。
怎么确认成了:本地 dist/index.html 存在、页面数对、npm run verify exit 0;Cloudflare 构建日志里能看到 verify 跑完,以及它内部触发的 build 成功,并生成了 dist。
4. 部署:Git 推送自动构建 vs 命令行直传
4.1 自动:push master
日常发布走这条:
git checkout master
git merge --ff-only dev
git push origin master
Cloudflare 只监听 master。push 到 dev 不会触发部署,这是最容易误会的一点。
Workers Builds 的配置:
- Build command:
npm run verify(构建在verify:site里) - Deploy command:
npx wrangler deploy - Preview builds:关闭
- 分支:
master
适合谁:日常发布、希望保留构建记录、希望平台帮你跑构建的人。
4.2 手动:本机 wrangler deploy
npm run build
npx wrangler deploy
适合谁:急修、调试、Cloudflare 构建队列卡住、需要立刻把本地验证过的产物传上去的人。
这条路的代价:你绕过了 Cloudflare 的构建闸门,所以本地必须先 npm run verify + npm run build。本地没验证就直传,等于把线上当测试环境。
怎么确认成了:Deployments 里出现最新记录,commit/时间对得上。但先别急着说“上线了”,看第 5 节。
5. Deployments 显示成功,为什么线上还是旧的
这是本文最重要的一节。
5.1 我们遇到的现象
今晚实际发生:
- Cloudflare Deployments 里有两条提交,一条 Queued 卡了 5 分钟,一条 Skipped;
- 点 Retry 也没救回来;
- 本地
npm run build成功,70 页; - 线上打开还是旧版:抓 HTML 能看到旧 class 名,新路由 404。
“构建成功”“部署记录存在”“线上已更新”是三件事,不是一件。
5.2 先按这个顺序查
- 看 commit,不看 badge:Deployments 里最新记录的 commit 是不是你刚推的那条?时间对不对?
- 看分支:是不是 push 到了
master?dev不触发部署。 - 看状态:
Queued是还在排队,Skipped是这次没构建;两个都不是“成功”。 - 绕过代理再抓页面:
curl --noproxy '*',抓 HTML 看新 class 名,访问新路由看是不是 404。 - 看部署列表:
npx wrangler deployments list,确认最新一次部署的时间和 commit。
如果前三步就发现是 Queued/Skipped,别等。等下去大概率还是白等。
5.3 这次怎么解决的
不是等,是本地 build 完直接传:
npm run verify
npm run build
npx wrangler deploy
实测 13 秒完成。传完再抓页面,新 class 名出现、新路由 200,才算真的更新。
为什么这条能成:它绕过了 Cloudflare 的构建队列,直接把本地 dist 上传。代价是本地必须自己承担验证责任。
5.4 验证时别被代理骗了
本机代理 127.0.0.1:7862 不稳定时,线上探测会返回 000 或 30 秒超时。站点其实是 200,代理把它伪装成挂了。
所有线上验证都加:
curl --noproxy '*' -sSI https://www.genwozuo.com/ | head -1
不加这个参数,你可能会把“代理坏了”误判成“Cloudflare 挂了”,然后白折腾一晚上。
怎么确认真的更新了:同时满足三条——最新 Deployments 的 commit 对、curl --noproxy '*' 返回 200、抓到的 HTML 里有这次的新 class 或新路由。少一条都不算。
6. 出问题时:构建卡住、代理、令牌
6.1 构建卡住或失败
先看 Cloudflare 构建日志,不是只看 Deployments 的颜色。常见原因:
- Node 版本不对:本地 24,平台也要 24;
- 依赖装不上:看是不是走了
npm ci、lockfile 有没有提交; - Build command 写错:
npm run build和npm run verify别混; - 超时或内存:构建时间突然变长,先看是不是多了大文件。
如果 Queued 卡很久、Retry 无效,直接用本地 wrangler deploy 救急;不要整晚等队列。
6.2 代理
curl一律加--noproxy '*';- Node 脚本如果必须走代理,用
NODE_USE_ENV_PROXY=1显式开; - 不要把代理的 000/超时当成站点故障,先换无代理再测。
6.3 令牌和权限
wrangler login只在本机做,不要把登录态提交到仓库;- CI/Cloudflare 用平台自己的凭据,不要往代码里塞 token;
- GitHub App 要能读对应仓库;Cloudflare token 按最小权限给;
- 永远不提交
.env、CLOUDFLARE_API_TOKEN、私钥。
具体 token 需要哪些 scope,我没逐项记录,未实测;按最小权限原则给,报 403 时先查账号、zone 和仓库权限。
6.4 回滚
线上坏了,先回滚,再查原因:
Cloudflare → Deployments → 选上一个 Ready → Rollback
界面没有入口时:
npx wrangler rollback
回滚后仍要跑第 5 节的线上验证,别假设回滚一定生效。
7. 上线检查清单
改 DNS 前
- 域名注册商能登录,知道 NS 在哪改
- 域名展示页已关闭
- 记下原 NS,万一要退回
- Cloudflare zone 已添加
push 前
-
npm run verifyexit 0 -
npm run build成功,dist/index.html存在 - 本地页面打开正常
- 分支是
master,不是dev
部署后
- Deployments 最新 commit 是这次提交
- 状态是 Ready,不是 Queued/Skipped
-
curl --noproxy '*'返回 200 - HTML 里有新 class/新内容
- 新路由不是 404
- apex 301 到 www
- 记下上一个 Ready,出事能回滚
8. 哪些是实测,哪些是未实测
| 部分 | 状态 |
|---|---|
| ename 买域名、关展示页、改 NS | 实测 |
| Cloudflare zone Active、apex/www 绑定、301 | 实测 |
| GitHub Actions、Cloudflare Workers Builds | 实测 |
本地 70 页构建、wrangler deploy 13 秒 |
实测 |
| Queued/Skipped、Retry 无效、线上旧版 | 实测 |
| 代理导致 000/30s | 实测 |
| 域名价格/续费 | 未实测 |
| token 具体 scope | 未实测 |
| 522/526 等 SSL 报错 | 未实测 |
下一步读什么
站点上线后,内容怎么从 0 开始,看从 0 开始:第一台自动化工作台;构建和命令基础,先看Node.js 安装。
如果遇到 ffmpeg 或 npm 的环境问题,分别看ffmpeg PATH 排错和npm install 报错。
常见问题
一定要用 Cloudflare 吗?
不一定。Vercel、Netlify 也能托管静态站。本文写的是我们实际走的 Cloudflare Workers 这条线,换平台时域名和构建部分可以借鉴,部署和验证要按平台改。
域名一定要改 NS 吗?
用 Cloudflare 的 Worker 自定义域名,域名要先接入 Cloudflare 成为一个 zone,改 NS 是最干净的方式。apex 域名做 CNAME 本来就受限,别在这上面绕。
Deployments 显示 Ready 就是更新了吗?
不是。Ready 只说明那一次构建部署完成,不保证它是最新提交,也不保证你访问到的是新版本。必须用无代理 curl 查页面内容和新路由。
可以直接 wrangler deploy 吗?
可以,适合急修和调试,能绕过 Cloudflare 的构建队列。但必须先在本地 build 并验证,因为你绕过了平台那道构建闸门。
为什么 curl 必须加 --noproxy '*'?
本机代理 127.0.0.1:7862 不稳定时,会把线上 200 伪装成 000 或 30 秒超时。不加这个参数,你会把网络问题当成站点挂了。
每周一张复习表
每周分享一张实用复习表,不收钱不打扰,想走随时走
邮箱存在 Cloudflare KV 里,只用来发每周更新;任何时候写信到hello@genwozuo.com说「退订」就删掉。详见隐私政策。