跳到正文
本页目录

域名搬完之后,我把静态博客放上了 Pages

作者 yilihjy发布于 阅读约 5 分钟

上一篇写的是域名迁移:从 Namecheap 转到 Cloudflare,$18.68 一年。域名搬完之后,下一步是站点托管。

这个博客是 Astro 生成的静态站:没有登录,没有数据库,没有按请求执行的代码;构建出来是 HTML、CSS、JS 和图片。

Cloudflare 的 Pages 和 Workers 都能托管站点。最后我用了 Pages。

静态站从 GitHub Actions 到 Cloudflare Pages,再到边缘和访客的路径;Worker 节点没有代码要跑。
静态站从 Actions 到 Pages,再到边缘;Worker 节点没有代码要跑。

为什么是 Pages,不是 Worker

Pages 和 Workers 的区别在请求进来之后有没有代码要跑。Pages 面向静态站;Worker 面向按请求执行的代码。这个站没有那段代码。

创建页的第一个入口是 Worker。这个站是静态站,Worker 用不上。

Worker 的 Static Assets 也能托管构建产物。代价是多一套资源配置,以及重定向、响应头、预览、回滚和自定义域的处理;这些在 Pages 里是默认行为。

Pages Worker
适合 静态站、前端 按请求执行的代码
部署 GitHub Actions GitHub Actions
预览 每次部署一个预览 URL 版本预览
回滚 控制台选上一个部署 版本回滚
重定向、响应头 Pages 的约定 自己实现
免费额度 静态请求、带宽不限 100k 请求/天
复杂度 低 高

以后要加评论、登录、动态 API,或者给 CMS 做认证入口,再单独加 Worker。博客的静态页仍然放在 Pages。

第一次部署

仓库是一个 pnpm workspace,博客在 apps/blog,构建产物在 apps/blog/dist。部署放在 GitHub Actions 里:主分支有 push 时,先装依赖,再跑一遍博客的校验,最后把 dist 传到 Pages。

校验不是只跑构建。产物断言、体积预算、内容校验都在里面;部署前再跑一遍,Pages 只接收已经通过校验的 dist。这样部署失败时,问题在构建或断言,不在「文件传了一半」。

Actions 需要两个凭据:只带 Pages 权限的 API token 和 account id。它们放在仓库的 secrets 里,不写进代码。

Cloudflare 里是一个 Pages 项目,生产分支是主分支。项目第一次通过 Direct Upload 建:在控制台上传一次 dist,项目就存在了;之后每次部署由 Actions 覆盖。控制台那次只用于创建项目。

部署失败或者需要回退时,Pages 控制台里选上一个成功部署即可。回滚不依赖 CI,也不依赖重新构建;每次部署都是一个完整的产物。

没有用 Cloudflare 的 Git 集成:它会消耗账号的 500 次/月构建额度,而构建在 Actions 里已经做过,CF 再构建一遍是重复;Actions 的校验门禁也不能跳过。

Pages 给每个项目一个 *.pages.dev 形式的默认域名,指向生产部署。每次部署还会生成一个 <hash>.*.pages.dev 形式的预览 URL:* 是项目名,<hash> 是这次部署的标识。预览 URL 和自定义域是两回事;前者用来在合并前看这次构建,后者是正式地址。

当前架构

当前的架构是这样的:访客经 Cloudflare 边缘到 Pages;自定义域直接到 Pages;默认域名先经过 Access;Actions 负责构建和上传。

flowchart TD
  accTitle: 当前网站架构
  accDescr: 访客通过 Cloudflare 边缘访问站点;自定义域直接到 Pages;默认域名先经过 Access;GitHub Actions 构建并上传静态产物。
  visitor["访客"] -->|HTTPS| edge["Cloudflare 边缘\nHSTS / nosniff"]
  edge -->|自定义域 apex / www| pages["Cloudflare Pages\n静态产物"]
  edge -->|默认域名 *.pages.dev| access["Cloudflare Access\n登录"]
  access -->|通过| pages
  actions["GitHub Actions\nmain push → 构建 + 校验"] -->|上传 dist| pages
  pages -->|返回页面| edge
  edge -->|www 301 到 apex| visitor

免费额度

这套东西的边际成本接近 0。免费额度分几项。

项目 免费额度 我的用法
Pages 静态请求、带宽 不限 全部
Pages Git 构建 500 次/月 不用(Direct Upload)
GitHub Actions 2000 分钟/月 远低于上限
Cloudflare Access 50 seats 1
DNS、SSL、HSTS 免费 全部
Worker 100k 请求/天 不用
域名 年费 一年一次

Pages 的静态请求和带宽不计费,这是选它而不是 Worker 的另一个理由。Worker 的免费额度按请求算,静态站放在 Worker 上,访问量会直接消耗 100k/天的额度;Pages 没有这个问题。

Actions 的 2000 分钟是私有仓的额度。这个仓库只在主分支和 PR 上跑检查和部署,开发分支不跑;实际用量远低于上限。

Access 的 50 seats 是用户数,不是访问量。给默认域名加一层登录,自己用 1 个 seat;被拦住的访客不占。

唯一固定的成本是域名年费,上一篇算过;其余都在免费额度里。

apex 和 HSTS

apex 是裸域,example.com,不带 www;自定义域是绑到 Pages 项目的域名,不是默认的 *.pages.dev。apex 自定义域就是把裸域绑到 Pages。

apex 有一条指向 Pages 的 DNS 记录;Cloudflare 做 CNAME flattening;Pages 里 apex 是 Custom domain。www 有一条指向 apex 的 DNS 记录,再加 301 到 apex;路径和查询串都保留。

SSL 用 Full (strict),Always Use HTTPS 开着。

HSTS 是 HTTP Strict Transport Security 的响应头。浏览器收到之后,在 max-age 内强制用 HTTPS,不再先走 HTTP;它防的是链路劫持和 SSL stripping。第一次访问仍然要靠 301,只有 preload 才会写进浏览器内置名单。includeSubDomains 把规则扩展到子域;preload 更硬,也更难回退。

HSTS 用 30 天,没有开 includeSubDomains,也没有开 preload。先短一点,确认没有子域还在 HTTP 上,再考虑加长。preload 的代价是长期的:域名进了浏览器名单,移除要等版本更新;个人博客没有必要。

Access 与安全

Pages 的默认域名是项目入口,也是预览入口。我给默认域名加了 Access,要求登录;正式的自定义域保持公开。

只保护默认域名,是因为它和自定义域在 Zero Trust 里分开管理。默认域名是部署入口,自定义域是读者访问的地址。默认域名关起来,预览和未合并的内容不会漏出去;自定义域仍然公开。

Access 的免费额度是 50 个用户。这里只用 1 个:自己的邮箱。被拦住的访客不占席位。

HSTS 的取舍是短 max-age 优先;可回退性比一次拉满更重要。

X-Content-Type-Options: nosniff 也开了。它让浏览器只认响应里的 Content-Type,不再猜文件类型;对静态站没有副作用。

证书是 TLS 1.3,apex 和 www 都有效。

验证

部署完,我先看 DNS:apex 和 www 都解析到 Cloudflare,NS 也对。然后看 HTTP:apex 是 200,www 301 到 apex,默认域名 302 到 Access 登录,http:// 301 到 HTTPS,不存在的路径是 404。最后看安全头,strict-transport-security 和 x-content-type-options 都在。

默认域名只作为部署和预览入口,不出现在站点的公开链接里。

边界

域名管地址,Pages 管静态产物,构建和校验交给 Actions,默认域名的访问控制交给 Access。

静态站用这套组合即可。以后要加动态能力,再加 Worker;静态站不必为「以后可能会有的需求」先背上一套运行时。

成本上,除了域名年费,其余都落在免费额度里。真正花时间的是构建、校验和维护,不是账单。

下一步,如果这个博客真的需要评论、登录或者动态 API,再考虑 Worker。现在没有。

参考

注:文中数字按 2026 年 10 月的情况算。