我用 Cloudflare Pages 搭了这整个站 — 新闻聚合、API 端点、数据库、自定义域名 — 总成本 $0。不是「免费试用期 $0」,是真正的、永久的、$0。这篇教程把从建仓库到上线运行的每一步都写清楚,包括我自己踩过的坑。
为什么选 Cloudflare Pages
我对比过几个主流的免费托管方案,最后选了 Cloudflare Pages,原因很简单:它不只是托管静态文件,而是给你一整套后端能力。
- 真正无限带宽 — 没有带宽上限,不像某些平台免费版限 100GB/月
- 全球 CDN — Cloudflare 在全球 300+ 节点,访问速度天然快
- Pages Functions — 在
functions/目录下写 JS 文件就能创建 Serverless API,无需单独部署后端 - D1 数据库 — Serverless SQLite,免费 5GB 存储,够个人站用很久
- 500 次构建/月 — 个人站绰绰有余
- 自定义域名 + 自动 SSL — 免费
对比一下:
- GitHub Pages — 纯静态托管,没有 Serverless Functions,没有数据库,带宽限 100GB/月
- Vercel 免费版 — 有 Serverless Functions,但带宽限 100GB/月,商业项目需付费
- Cloudflare Pages — 无限带宽 + Functions + D1 数据库,免费版对个人站几乎没有实质限制
Step 1 — 准备 GitHub 仓库
Cloudflare Pages 通过 GitHub 仓库拉取代码。第一步就是创建一个仓库并推送你的网站文件。
# 创建项目目录
mkdir my-site && cd my-site
git init
# 创建一个最简单的首页
cat <<'EOF' > index.html
<!doctype html>
<html>
<head><title>My Site</title></head>
<body><h1>Hello World</h1></body>
</html>
EOF
# 推送到 GitHub
git add .
git commit -m "init: first page"
git remote add origin https://github.com/your-name/my-site.git
git push -u origin main
就这么简单。不需要 package.json,不需要构建脚本,不需要任何框架。一个 HTML 文件就能部署。
Step 2 — 连接 Cloudflare Pages
接下来把 GitHub 仓库和 Cloudflare Pages 连起来:
- 注册 Cloudflare 账号(免费)
- 进入 Dashboard → Workers & Pages → Create
- 选择 Pages → Connect to Git
- 授权 GitHub,选择你的仓库
- 构建设置:
- Build command:留空(纯静态站不需要构建)
- Build output directory:
/(项目根目录就是输出目录)
- 点击 Save and Deploy
大约 30 秒后,你的站就上线了。Cloudflare 会分配一个 *.pages.dev 的临时域名,比如 my-site-abc.pages.dev,可以直接访问。
从现在开始,每次你 push 代码到 main 分支,Cloudflare Pages 都会自动部署。不需要手动操作。
Step 3 — 绑定自定义域名
用 *.pages.dev 域名也能凑合,但自定义域名更专业。步骤:
- 在 Cloudflare Pages 项目的 Custom domains 标签页点击 Set up a custom domain
- 输入你的域名(比如
cyrustyj.xyz) - 如果你的域名 DNS 已经托管在 Cloudflare(推荐),CNAME 记录会自动创建
- 等待 DNS 生效(通常几分钟),SSL 证书自动签发
我第一次部署时,Cloudflare Pages 显示部署成功,
*.pages.dev 域名也能正常访问,但自定义域名 cyrustyj.xyz 返回 403 Forbidden。排查了半天,原因很简单:部署成功不代表域名自动绑定。你必须在 Pages 项目的 Custom Domains 里手动添加你的域名。Cloudflare 不会自动把你的域名关联到 Pages 项目,即使这个域名的 DNS 就在 Cloudflare 上。
Step 4 — Pages Functions(免费 API)
这是 Cloudflare Pages 最让我惊喜的部分:不需要单独部署后端,在项目里放一个 JS 文件就能创建 API 端点。
文件路由
Pages Functions 用文件路径决定 API 路由:
目录结构 → API 路由functions/
api/
visits.js → /api/visits
favorites.js → /api/favorites
news.js → /api/news
放进去,push 到 GitHub,API 就自动上线了。
实际例子:访问计数器
这是我这个站真实在用的访问计数 API(简化版):
functions/api/visits.js(简化版)export async function onRequest(context) {
const { request, env } = context;
const url = new URL(request.url);
const path = url.searchParams.get("path") || "/";
const db = env.DB; // D1 数据库绑定
if (!db) {
return Response.json({ ok: true, totalVisits: 0 });
}
// 记录访问
const today = new Date().toISOString().slice(0, 10);
await db.prepare(
"INSERT OR IGNORE INTO page_visits (path, visit_date) VALUES (?, ?)"
).bind(path, today).run();
// 查询统计
const row = await db.prepare(
"SELECT COUNT(*) AS count FROM page_visits WHERE path = ?"
).bind(path).first();
return Response.json({
ok: true,
path,
totalVisits: row?.count || 0
});
}
核心就这么多。onRequest 是 Pages Functions 的入口函数,context 里包含 request(HTTP 请求)和 env(环境变量和数据库绑定)。
fetch、Request、Response、URL、crypto)都能用,但 fs、path 等 Node.js 专有模块不可用。好消息是,写 API 端点基本不需要那些模块。
Step 5 — D1 数据库
光有 API 还不够,数据总得存在某个地方。Cloudflare D1 是一个 Serverless 的 SQLite 数据库,免费版给 5GB 存储。
创建 D1 数据库
- 在 Cloudflare Dashboard → Workers & Pages → D1 SQL Database → Create
- 给数据库起个名字,比如
my-site-db - 创建完成后,记住数据库的 ID
绑定到 Pages 项目
回到 Pages 项目 → Settings → Bindings → Add → 选择 D1 Database:
- Variable name:
DB(这就是你在 Functions 里通过env.DB访问数据库的名字) - D1 Database:选择你刚创建的数据库
D1 绑定添加后,已部署的版本不会自动获得绑定。你需要触发一次新的部署(push 一个 commit 或手动在 Dashboard 点 Retry deployment)。否则
env.DB 会是 undefined,你的 Functions 找不到数据库。
表结构示例
你可以在 D1 的控制台直接执行 SQL,也可以在 Functions 里自动建表。这是我这个站用的访问记录表:
SQL — 建表CREATE TABLE IF NOT EXISTS page_visits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT NOT NULL,
visit_date TEXT NOT NULL,
visitor_hash TEXT NOT NULL,
created_at TEXT NOT NULL,
UNIQUE(path, visit_date, visitor_hash)
);
CREATE INDEX IF NOT EXISTS idx_visits_path
ON page_visits(path);
CREATE INDEX IF NOT EXISTS idx_visits_path_date
ON page_visits(path, visit_date);
D1 用的是标准 SQLite 语法,如果你写过 SQL,上手零成本。
Step 6 — GitHub Actions 自动更新
到这里你已经有了一个带 API 和数据库的静态站。但如果你想要「动态内容」呢?比如这个站的 AI 新闻聚合,每天自动更新两次。
秘密很简单:用 GitHub Actions 定时跑脚本更新数据文件,commit 并 push,Cloudflare Pages 自动重新部署。
.github/workflows/update-news.yml(简化版)name: Update AI News
on:
schedule:
# UTC 01:00 / 13:00 → 北京时间 09:00 / 21:00
- cron: "0 1,13 * * *"
workflow_dispatch: # 手动触发
jobs:
update-news:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Build news data
run: python scripts/update_news.py
- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add data/news.json
# 只在有变更时提交
git diff --staged --quiet || git commit -m "chore: auto-update AI news"
git push
这个工作流每天在北京时间 09:00 和 21:00 执行。脚本从各个 API 拉取新闻数据,生成 JSON 文件,提交到仓库。GitHub push 触发 Cloudflare Pages 自动部署 — 用户看到的是最新的内容。
成本清单
把这个站所有用到的服务列出来:
| 服务 | 用途 | 免费额度 | 实际费用 |
|---|---|---|---|
| Cloudflare Pages | 静态托管 + CDN | 无限带宽,500 次构建/月 | $0 |
| Pages Functions | Serverless API | 10 万次请求/天 | $0 |
| Cloudflare D1 | 数据库 | 5GB 存储,500 万行读取/天 | $0 |
| GitHub Actions | 定时构建 | 2000 分钟/月 | $0 |
| 自定义域名 | cyrustyj.xyz | Cloudflare 成本价注册 | ~$9/年 |
| SSL 证书 | HTTPS | 自动签发 | $0 |
除了域名注册费(严格来说不到 $1/月),运行成本是零。如果你不介意用 *.pages.dev 域名,连域名费都省了。
踩坑记录
除了前面提到的 403 和 D1 绑定问题,再补充几个:
Pages Functions 的日志在 Dashboard 里不太直观。调试 API 时建议在本地用
wrangler pages dev 启动开发服务器,可以看到完整的 console.log 输出。安装方式:npm install -g wrangler。
D1 底层是 SQLite,不是 MySQL 或 PostgreSQL。一些习惯性写法可能不兼容,比如
UPSERT 语法、日期函数、JSON 函数的行为都跟 MySQL 不同。遇到报错先查 SQLite 文档,不要照着 MySQL 教程写。
GitHub Actions 的 schedule 触发通常会延迟 5-30 分钟(官方说法:「负载高时可能延迟更多」)。不要依赖它做精确定时任务。对新闻更新来说,延迟半小时完全无所谓,但如果你需要精确调度,考虑用 Cloudflare 自带的 Cron Triggers。
总结
整个流程回顾:
- GitHub 仓库 — 放你的 HTML/CSS/JS 文件
- Cloudflare Pages — 连接仓库,自动部署,全球 CDN
- Custom Domains — 绑定自定义域名(记得手动添加,不会自动关联)
- Pages Functions —
functions/目录下写 JS = Serverless API - D1 数据库 — 创建后绑定到 Pages 项目,通过
env.DB使用 - GitHub Actions — 定时更新数据 + 自动部署 = 静态站的动态内容
对个人站来说,这套组合基本是目前能找到的最优免费方案。不用管服务器、不用管扩容、不用管 SSL — push 代码就上线。