2766 字
14 分钟
GitHub + Cloudflare Pages:零成本、无备案、全球 CDN 的个人博客

个人博客这件事最容易犯的错,是一上来就想”我要做个什么”。

我这次反过来做:先确定”最省事的方案能长什么样”,再倒推技术选型。 结果是:

成本 ¥0(Cloudflare 侧;域名另算,见下)
域名 可以完全不买(用免费的 *.pages.dev)
备案 不用(原因后面讲清)
服务器 不用(Pages 是静态托管)
CDN Cloudflare 全球 anycast,默认就有
HTTPS 自动签发,自动续期

整套东西的核心就一句话:

GitHub 存源码,Cloudflare Pages 跑构建,两边一个 push 打通。


一、整条链路长什么样#

sequenceDiagram autonumber participant ME as 我(本地) participant GH as GitHub 仓库 participant CF as Cloudflare Pages participant CFCDN as Cloudflare 全球 CDN participant U as 读者 ME->>ME: 本地 pnpm build 验证 ME->>GH: git push main GH->>CF: Webhook 通知(仓库变更) CF->>GH: 拉取源码(按 commit SHA) CF->>CF: 按 .nvmrc / package.json 装环境 CF->>CF: pnpm install → pnpm build CF->>CF: 产出 dist/ 静态文件 CF->>CF: 原子替换上线(发布 Production 部署) CF-->>GH: 部署状态回写 commit status U->>CFCDN: 访问 https://ycxdev.com CFCDN->>CFCDN: 就近边缘节点命中缓存 CFCDN-->>U: 返回 HTML/CSS/JS(自动 HTTPS) Note over U,CFCDN: 读者不需要回源,成本也不按流量计费

几个值得注意的点:

  • Cloudflare 主动拉代码,不是 GitHub 推产物。所以你的密钥不需要交给 GitHub Actions,Cloudflare 用的是它自己跟 GitHub 的 OAuth 授权。
  • 部署是原子的:新构建完整成功后才切换,失败不影响线上版本。这一点比很多自建方案强 —— 你 push 了一个坏 commit,线上还是好的。
  • 每次 push 都有独立的部署 URL,除了 production 还有一堆 preview 部署,24 小时后自动清理。想看草稿效果随便推。

二、免费额度到底有多少#

Cloudflare Pages 免费版(Free plan)的关键数字:

项目免费额度说明
站点数量无限制
构建次数500 次 / 月个人博客一天推几次都绰绰有余
单次部署文件数20,000
单文件大小25 MiB所以图片别往仓库里塞特大的原图
构建超时20 分钟静态博客一般几秒
带宽不限流量Pages 不按流量计费,这是它最大的优势
自定义域名100 个 / 项目
TIP

“不限流量”这一条要重点说一下。很多静态托管(Vercel Hobby、Netlify 免费版)都有流量/构建额度限制,超了就限速或停服。Cloudflare Pages 免费版不按流量收费 —— 这也是我最后选它的主要原因之一。

关键约束:注册时不要绑信用卡。 绑卡后会进入需要验证的账户体系,一堆原本免费的功能会变成”需要开通付费”。这个博客从头到尾只在免费额度里跑。


三、为什么不用备案#

这个是很多国内用户的顾虑,讲清楚原理就不神秘了。

ICP 备案的触发条件是两个同时成立:

① 网站内容/服务器 面向中国大陆境内用户提供服务
② 服务器 部署在中国大陆境内(境内 IP 的机房)
备案才需要。

而 Pages 的机器在 Cloudflare 的全球 anycast 网络上(境外节点)。你的域名解析过去,落到的是境外机房的 IP,不满足第 ② 条,所以不需要备案。

WARNING

需要避开的坑:

  • 不要开 Cloudflare 中国网络(China Network) —— 那个是企业版付费功能,明确要求域名已完成 ICP 备案。开了反而会要求你有备案。
  • 如果你买的是 .cn 域名并做实名认证,注意 .cn 域名本身在境内就有实名要求,这跟站点备案是两件事。

我的站点现在跑在 ycxdev.com 上,走的就是海外 anycast。国内访问速度取决于运营商和跨境链路,但这不是”不合规”的问题,只是延迟问题。

那域名要不要买#

分两种情况:

用免费子域名用自己买的域名
地址xxx.pages.devycxdev.com
花钱¥0域名年费(.com 通常几十块/年)
备案不需要仍然不需要(托管仍在境外)
换托管商只能改 Cloudflare 项目名改 NS 记录,随时搬
邮件 / 其他服务用不上可以拿去做邮箱、Redirect 等
Open Graph 观感一般好

先用 *.pages.dev 把内容跑起来,等真的有内容了再买域名。 我一开始也是纯 pages.dev,后来才换的 —— 换域名只是加一条 DNS 记录 + 在 Pages 里绑一次,站点内容一个字都不用动。

NOTE

换域名之后有件事必须手动改,否则线上会静默出问题:

astro.config.mjs 的 site: → canonical / sitemap / RSS 的绝对地址
public/admin/config.yml 的 site_url → CMS 里生成的链接
.github/workflows/indexnow.yml → 推给搜索引擎的 host 和站点地址
CF 上绑定的 Web Analytics 站点

这些地方都拿”站点绝对地址”来拼 URL。改域名时漏改一个,canonical 就会指向旧域名,搜索引擎会当成两个站处理。

WARNING

需要避开的坑:

  • 不要开 Cloudflare 中国网络(China Network) —— 那个是企业版付费功能,明确要求域名已完成 ICP 备案。开了反而要求你有备案。
  • .cn / .com 在境内注册商买域名要做域名实名认证,但这是”域名实名”,跟”网站 ICP 备案”是两件不同的事,别混。
  • 域名买了不等于站点备案了,也不等于需要备案 —— 取决于你的服务器在哪。

四、顺手白嫖的 Cloudflare 设施#

Pages 只是入口。同一个账号下免费额度里还有一堆能直接用的东西,我都用上了:

flowchart LR A["Cloudflare 免费账号"] --> B["Pages
静态托管 + CDN"] A --> C["Pages Functions
10万请求/天"] A --> D["KV
1000 写入/天"] A --> E["D1 (SQLite)
500万行读/天"] A --> F["Workers
10万请求/天"] A --> G["Web Analytics
无 Cookie 无采样"] A --> H["Turnstile
人机验证"] B --> C C --> D C --> G

具体我这个博客用到的:

1. Pages Functions + KV 做阅读量#

纯静态站点没法记”这篇文章被看了多少次”,因为静态文件没有后端状态。用 Pages Functions(本质是 Workers)当后端,数据丢 KV:

// functions/api/views.ts (简化)
export async function onRequestGet({ request, env }) {
const path = new URL(request.url).searchParams.get("path");
const key = `v:${path}`;
const n = parseInt((await env.VIEW_COUNTS.get(key)) ?? "0") + 1;
await env.VIEW_COUNTS.put(key, String(n));
return Response.json({ views: n });
}
WARNING

KV 免费额度只有 1000 次写入 / 天。 一个写入对应一次自增。所以这个计数接口必须做防重 —— 我在前端用 lastCountedPath 保证同一次浏览只计一次。超额了 KV 会直接拒绝写入(不会扣费,但会报错)。这也是为什么阅读量要”点开才显示”而不是放在首屏阻塞渲染。

2. Web Analytics#

Cloudflare 的 Web Analytics 免费、无 Cookie、不采样,而且关键是:

它不要求站点托管在 Cloudflare。 *.pages.dev 这种不属于你自己 zone 的域名也能用(拿不到 zone 级功能,但 RUM 可以)。

打开方式就两行:

<!-- Cloudflare 自动注入的 beacon -->
<script defer src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token":"<你的 32 位 token>"}'></script>
NOTE

那个 token 本身就是公开的 —— 它必须出现在前端 JS 里才能工作。所以看到别人页面上有这个 token 不构成安全问题,不用当成密钥来藏。

3. OAuth 中转 Worker#

我给博客配了一个 Sveltia CMS,需要 GitHub OAuth 登录。GitHub OAuth 的回调地址必须是注册过的绝对 URL,用 localhost 开发时很麻烦。我用一个免费 Worker 做中转,把回调先落到 Worker 上再 302 回本地。

这是 Workers 免费额度(10 万请求/天)最典型的用法。

4. 用不到的(因为要绑卡)#

产品为什么跳过
Cloudflare R2需要绑卡开通
Cloudflare Images需要绑卡
Cloudflare Stream需要绑卡
Workers Paid / Pages 付费增强需要绑卡
China Network需要备案 + 企业合同

五、Cloudflare Pages 部署 Astro 的几个坑#

Astro 是 Cloudflare 官方推荐的框架之一(他们有专门的 Pages 集成文档),但纯静态输出用 Pages 时不需要 adapter。我踩过的坑:

坑 1:Node 版本不一致导致构建失败#

Cloudflare 默认用自己固定的 Node 大版本。本地 node -v 是 20,构建机上可能是 22,某些原生依赖会挂。

解法:加 .nvmrc

20

坑 2:包管理器必须一致#

用 pnpm 就要让 Cloudflare 也用 pnpm。有两种做法:

Terminal window
# 仓库根目录加一个 corepack 声明
"packageManager": "[email protected]"

坑 3:自定义响应头(CSP)用错文件名#

Cloudflare Pages 的自定义 header 文件名必须是 _headers,放在 public/ 下,会被原样拷到 dist/ 根目录:

public/_headers → dist/_headers → 边缘节点生效
/*
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
TIP

CSP 一开始我是开着的,结果被内联的 analytics beacon 和几个第三方资源全部挡掉。先上 Content-Security-Policy-Report-Only 观察一段时间,确认没有违规再切成强制模式。

坑 4:构建失败时线上是完好的#

这不是坑,是优点,值得单独强调:Cloudflare Pages 的部署是原子的。构建失败就停在原版本,线上不受影响。我有一次 commit 写崩了 markdown 配置,GitHub 上 CI 红了,站点还是好的 —— 改完再推就恢复了。


六、为什么源码一定放 GitHub#

这一点我一开始想错过,后来发现是整个方案的地基。

Cloudflare Pages ┐
├──→ 内容以 GitHub 仓库为唯一来源(single source of truth)
Sveltia CMS ┘
  • Pages 只是构建产物。删了重建一条命令,不丢任何东西。
  • CMS 也只是编辑器,它提交的变更最终是往仓库里 push Markdown 文件。所以 CMS 挂了不影响发布。
  • 有完整历史。哪天想回退到三个月前的某个版本,git revert 一下,Cloudflare 自动重新构建。
  • 迁移成本低。以后想换别的静态托管(Vercel / Netlify / 自建),换的是部署配置,内容文件一个字都不用改。
NOTE

我的仓库是 Private。内容里有私人记录,而且 Cloudflare 的 GitHub 集成支持私有仓库 —— 构建机上拿的是只读的短期凭据,源码不会因为部署而泄露。


小结#

  • 架构就一句话:GitHub 是唯一真源,Cloudflare Pages 负责构建和 CDN,中间一个 webhook 打通。
  • 成本 ¥0:不绑卡、不用服务器、不备案(域名可选,先白嫖 *.pages.dev 也行)。
  • 不限流量是 Pages 免费版最大的卖点,500 次构建/月对个人博客远远够用。
  • 不备案的原因:备案要求”境内服务器”,Pages 在境外 anycast,两者不同时成立。
  • 顺手白嫖:KV + Functions 做阅读量、Web Analytics 做统计、Workers 做 OAuth 中转、Turnstile 防机器人。
  • 避开要绑卡的:R2 / Images / Stream / Workers Paid / China Network。
  • 小心 Node 版本、包管理器、_headers 文件名这三个构建坑。

现在这个博客从推代码到全球可访问,全程不超过两分钟,成本为零。剩下的时间全用来写东西 —— 这才是博客该有的样子。

GitHub + Cloudflare Pages:零成本、无备案、全球 CDN 的个人博客
https://ycxdev.com/posts/github-cloudflare-pages-free-blog/
作者
YCX
发布于
2026-10-13
许可协议
CC BY-NC-SA 4.0