这是一篇个人网站的完整部署记录:作者用 Astro + Supabase + Cloudflare Workers 的全托管组合,零成本搭起一个支持中英日三语的博客。文章分两半——前半是从零开始的部署步骤,照着做能得到一模一样的站;后半是九条真实踩坑记录,讲清每一步的原因和解法。核心卖点是「零运维、发布即上线、月成本 0 元」。
从零到上线:个人网站的完整部署记录
写在前面
这篇文章是这个网站的「自白书」——完整记录它是怎么从一台本地电脑里的一行行代码,变成你此刻正在浏览的这套页面的。内容分两部分:前半是从头开始的完整部署步骤(照着做,你能得到一个一模一样的站);后半是真实的踩坑记录(每一步坑我都踩过,并写下了原因和解法)。
适合谁读:想零成本搭一个个人博客的人;或者未来的我自己——当某天我又想把一切推倒重来时,希望不用再从零摸索。
成果速览:这是一个什么样的网站
先看建成后的样子,有个整体印象:
| 项目 | 现状 |
|---|---|
| 框架 | Astro 4(SSR 服务端渲染模式) |
| 数据库 | Supabase(Postgres,文章/评论/分类/站点配置全在这里) |
| 托管 | Cloudflare Workers(免费额度:10 万请求/天、无限带宽、3000 构建分钟/月) |
| 域名 | 自购域名,DNS 托管在 Cloudflare,HTTPS 自动签发 |
| 语言 | 中文 / English / 日本語 三语同源 |
| 写作后台 | 自带 /admin 后台,浏览器里直接写、直接发 |
| 月成本 | 0 元(域名是唯一历史支出,其余全部免费档) |
它的工作方式用一句话说清:读者访问的永远是 Cloudflare,Cloudflare 在每一个请求发生时实时去 Supabase 查文章内容返回给你。所以后台点「发布」的那一刻文章就上线了,不需要等待构建、不需要重新部署——这是整套设计里我最满意的一点。
读者浏览器 ──► Cloudflare Worker(SSR 渲染 + 静态资源)
│ 每个请求实时查询
▼
Supabase(文章 / 评论 / 分类 / 配置)
▲
后台 /admin ──────────┘ (浏览器直连 Supabase,发布即生效)
方案对比:为什么选这套,以及它的不足
选型不能只看优点。把主流方案摆在一起如实比较,你才能判断这套是否适合你。
与主流方案的横向对比
| 维度 | 本方案(Astro + Supabase + CF Workers) | WordPress | Next.js + Vercel | Hexo / Hugo(纯静态) |
|---|---|---|---|---|
| 服务器运维 | 零,全托管 | 要(或买托管服务) | 零 | 零(但发文要本地构建) |
| 发文流程 | 浏览器后台直接发,即时生效 | 后台直接发 | 同左 | 本地写 → 构建 → 推送 |
| 在线写作 | ✅ 自带后台 | ✅ | 需另接 CMS | ❌ |
| 数据库 | Supabase(Postgres,完整 SQL) | MySQL | 随缘(常接外部 CMS) | 无(文件即内容) |
| 免费额度 | CF 10 万请求/天 + Supabase 免费档 | 无(需主机费) | 有(商用受限) | 静态托管几乎无限 |
| 国内访问 | CF 边缘偏慢,可用 | 看主机 | 同样不稳 | 可放国内 CDN,最快 |
| 生态/资料 | Astro 生态较小,中文资料少 | 最大,插件主题随便挑 | Next.js 生态最大 | 静态生成生态成熟 |
| 数据在手 | 在 Supabase,可随时导出 | 在自己库 | 看接什么 | 就是本地文件 |
这套组合的真实优势
- 零成本且零运维:没有服务器要补丁、没有数据库要备份脚本、证书自动续期。个人站最大的隐性成本其实是「怕它挂了」,这套方案把这项也归了零。
- 发布即上线:SSR 每请求现查,写完点发布的那一刻读者就能看到。纯静态方案「本地构建 → 推送 → 等部署」的链条在这里不存在。
- 自带后台 + 数据库能力完整:文章、评论、分类注册表、站点配置全在 Postgres 里,RLS 把「谁能读什么」管得明明白白,还能直接写 SQL 做统计——这是文件型方案给不了的。
- 三语同源、SEO 内置:RSS、sitemap、canonical、hreflang 都是架构自带的,不是插件拼的。
也必须说清楚的劣势
- Astro 生态比 Next.js 小:遇到冷门问题时,能搜到的中文方案有限,更多时候得读英文文档或直接问 AI。
- 国内访问 Cloudflare 边缘「能用但偏慢」:这是 CF 大陆网络的客观现状,读者侧体感比 Vercel/Netlify 或国内 CDN 差一截。我对这个站的定位是「记录自己」,没有追求国内极速;如果你做的是面向国内读者的内容站,这条要认真权衡。
- 后台发文看网络脸色:后台页面在浏览器里直连 Supabase,国内网络不稳时登录/保存可能转圈,换个顺畅的网络即可,读者侧无感知。
- Supabase 免费项目 7 天无活动会暂停:需要进后台点一下唤醒。对周更的博客无影响,但「年更」选手要记得。
- 默认无页面级缓存:发布零延迟的代价是每个请求都实时渲染(好在 SSR 通常远低于 10ms 上限)。真需要缓存时自己加
s-maxage即可,个人站当前没必要。 - 长尾风险:免费档的额度政策(Cloudflare / Supabase)未来若调整,可能需要迁移或付费——这是所有「白嫖流」方案的共同风险,要有心理预期。
一句话总结:如果你要一个「自己的、能长期写的、不折腾的」个人网站,这套很合适;如果你追求国内访问速度或需要丰富插件生态,WordPress 或静态方案可能更省心。
我的建站简史(为什么是第三次)
这不是我第一次拥有个人网站。
2018 年,我在 GitHub 上第一次接触到开源世界,跟着一个博客项目的 README 在本地一步步复现,成功跑起来的那一刻,新世界的大门被打开了。后来我在阿里云买了自己的域名和服务器,薅着免费的云存储和 CDN,把网站部署了上去。因为监管要求,完成工信部 ICP 备案和公安网安备案之后,网站才终于正式上云。那几年我写着代码之外的生活,直到因为成本原因,在五年后关闭了网站,注销了备案。
2026 年 10 月,AI 编程能力已经成熟到普通人可以凭对话构建系统。我决定再搭一次——但这次彻底换思路:传统的 WordPress、Hexo 那一套并不契合我的需求(一个要服务器运维,一个每次发文都要本地构建)。我最终选了一套「全托管、零服务器」的组合:代码放 GitHub,Cloudflare 托管,Supabase 存数据,前端静态页面 + 后端管理页直接发文。对于一个访问量不大的个人网站,这些服务的免费额度绰绰有余。
于是有了你正在看的这个站。下面进入正题。
第一步:准备工作
账号清单
| 服务 | 用途 | 收费情况 |
|---|---|---|
| GitHub | 代码仓库,部署触发器 | 免费 |
| Cloudflare | 托管 Worker、DNS、HTTPS | 免费 |
| Supabase | 数据库 + 登录鉴权 | 免费 |
| 一个域名 | 门牌号 | 已购则无新增费用 |
本地环境
- Node.js 22(仓库有
.nvmrc锁定,nvm use即可) - git、npm
- 一个能顺畅访问 GitHub 与 Supabase 的网络环境(后面会说为什么)
第二步:搭数据库(Supabase)
- 注册 Supabase,New project 新建项目。区域随便选(个人站距离不敏感),数据库密码自己记住。
- 项目就绪后进入 SQL Editor,把仓库
supabase/目录下的五个 SQL 文件按下面的顺序逐个执行(每个都可重复执行,报错「已存在」是正常的)。一个都不能少,缺任何一个都会让后台对应功能报错:schema.sql:核心基础。建文章、评论、站点配置、分类旧链接映射等核心表,配好 RLS 行级安全策略、评论相关的 RPC 函数,以及一个公开读的images图床桶。其余文件都在它之上叠加,所以必须第一个跑。categories.sql:建分类注册表。分类以注册表为权威来源,支持新增/重命名/合并/删除,重命名后旧链接自动 301,不会 404。posts_author.sql:给文章表补author作者列。别跳过,否则后台发布文章会报错。media_library.sql:建文件管理所需的记录表。让每个上传的图片/附件都能记下「属于哪篇文章」,删文章时据此精确清理,避免存储里堆一堆无人引用的孤儿文件。posts_trash.sql:给文章加软删除能力。删除文章改为「移入垃圾箱」,保留 5 天后自动彻底清除,期间可恢复或手动彻底删除。
- 进入 Project Settings → API,复制两样东西,等会要用:
Project URL:形如https://xxxx.supabase.coanon publickey:一长串eyJ...开头的密钥
- 创建管理员账号:Authentication → Users → Add user,填你的邮箱和密码,勾选自动确认。这就是后台
/admin的登录凭证——本站后台没有独立注册体系,直接用 Supabase 账号体系,一个人站不需要更复杂的了。
关于
anonkey 的安全性:它本就设计为公开,随前端下发。真正的安全边界是 RLS——读者只能读到「已发布文章」和「已审核评论」,写操作也被策略层强制校验(昵称/邮箱/内容长度与格式)。敏感操作(AI 服务的 API key 等)另放 Cloudflare Secret,不下发浏览器。
第三步:把网站在本地跑起来
git clone https://github.com/<你的用户名>/<仓库名>.git
cd <仓库名>
nvm use # 或确保 Node 22
npm install
复制 .env.example 为 .env,填入第二步拿到的两样:
PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
PUBLIC_SUPABASE_ANON_KEY=eyJ...你的anon key
# 站点对外地址(影响 sitemap / RSS / canonical);本地开发可先留空
PUBLIC_SITE_URL=
然后:
npm run dev # http://localhost:4321
没配 .env 也能跑——站点会降级用内置的三语示例文章,方便你先看效果。想验证「连真实数据库」的完整效果,再填上 .env。
本地预览生产构建产物用 npm run build && npx wrangler pages dev dist(Workers 本地模拟,和线上运行时一致)。
第四步:推上 GitHub
git add .
git commit -m "init"
git remote add origin https://github.com/<你的用户名>/<仓库名>.git
git push -u origin main
后面所有「改代码」的流程都一样:本地改 → commit → push。推送这个动作本身就是部署触发器。
第五步:部署到 Cloudflare Workers(核心步骤)
⚠️ 这一步有一个大坑前置提醒:创建项目时要选 Workers,不是 Pages。本站是 SSR 应用(依赖构建生成的 Worker 运行时),必须走 Workers。选错了后面会报
Workers-specific command in a Pages project,得删了重建。
- Cloudflare Dashboard → Workers & Pages → Create → Connect to Git,授权 GitHub,选仓库,Production branch 选
main。 - 构建设置(Framework preset 选 Astro 通常会自动填好):
- Build command:
npm run build - Build output directory:
dist - Deploy command:
npx wrangler deploy← 由仓库根目录的wrangler.toml驱动,不要改
- Build command:
- Settings → Builds → Build environment variables(注意是构建变量区,不是 Runtime 变量区)添加四个:
变量 值 说明 PUBLIC_SUPABASE_URLhttps://xxxx.supabase.co必填 PUBLIC_SUPABASE_ANON_KEYeyJ...必填 PUBLIC_SITE_URL先填 https://<项目名>.workers.dev建议,绑域名后再改 NODE_VERSION22与 .nvmrc双保险这三个
PUBLIC_*是构建时内联变量——值在npm run build那一刻写进产物,所以建站阶段改了它们必须重新部署才生效。NODE_VERSION不一样:它只决定构建阶段用哪个 Node,不会进产物,改了同样需要重新部署。 - Save and Deploy,等 1–3 分钟,得到
https://<项目名>.workers.dev。 - 以后:只有改代码才需要 push。发文章不触发任何构建。
wrangler.toml 仓库里已带好,核心就是这么几行,无需手动配:
name = "mystyle"
main = "dist/_worker.js/index.js"
compatibility_date = "2025-07-18"
compatibility_flags = ["nodejs_compat"]
[assets]
directory = "./dist"
实际文件里还会有 account_id 和 [vars] 段(后者放非敏感的运行时配置)。account_id 是你自己 Cloudflare 账号的值,不要照抄本文的;换仓库或换账号时记得改。
其中 nodejs_compat 是 SSR 的生命线——缺了它,Worker 里任何 Node 内建调用都会直接 500。
第六步:绑定自定义域名
域名是我早年在阿里云买的(.cn)。接入流程:
- 把域名托管到 Cloudflare(2025 年控制台改版后的新路径):Dashboard 左侧 Domains → Onboard a domain → 输入 apex 域名 → Continue。套餐选 Free。
- Cloudflare 会给两个 NS 服务器(
xxx.ns.cloudflare.com)。到域名注册商处,把域名的 DNS 服务器(NS)修改为这两个地址——各注册商后台都有这个入口,一般在「域名管理 → DNS 修改 / DNS 服务器」一类的菜单里。 - 等 CF 面板里域名状态变 Active(通常几分钟到几小时,NS 生效时间)。
- Workers 项目 → Settings → Triggers → Custom Domains → Add,填你的域名。DNS 在 CF 则记录自动配好,HTTPS 证书自动签发,全程免费。
- 最后一步别忘:把构建环境变量里的
PUBLIC_SITE_URL改成https://你的域名,然后 Retry deployment(Deployments 页最新部署 →···→ Retry)。这样 sitemap、RSS、canonical 里的域名才全部正确。文章内容不受影响。
第七步:登录后台,发第一篇文章
浏览器打开 <域名>/admin,用第二步建的 Supabase 账号登录。后台分四个模块:
- 文章管理:写标题、选语言(中/英/日)、分类、正文(Markdown 实时预览),点「发布并上线」。发布即生效——去前台刷新,文章已经在等你。编辑器「更多设置」里还能改发布时间(前台展示、排序、RSS 同步更新)。两个容易困惑的地方先说明:
- 点语言按钮是「切换版本」:会直接载入该语言已发布的标题、摘要、正文和 slug,而不是只改一个语言标识。所以切过去看到别的语言的内容,是正常的。
- 删文章是进垃圾箱:不是立刻消失,保留 5 天后自动清除,期间可以恢复或手动彻底删除。
- 文件管理:上传图片与附件,自动记录每个文件归属哪篇文章;删除文章时联动清理,不会在存储里留下无人引用的孤儿文件。
- 评论管理:审核、回复(管理员回复有专属标记,嵌套展示)、删除。
- 其他功能:首页倒计时、摘要生成设置、分类管理(重命名自动 301)、站点头像上传(含历史头像切换与回看)。
顺带说一句评论的设计:评论表收集邮箱但不公开,仅博主后台可见;匿名评论者会拿到一个删除令牌,能管理自己发过的评论——不需要人人注册。
第八步:三语与 SEO
三语是这套架构自带的能力,不需要额外服务:每语言有独立 RSS、整站 sitemap,配好 canonical 与 hreflang。各语言版本是独立记录,在后台切换语言分别撰写与发布即可。某个语言版本尚未发布时,前台显示提示横幅并保留已有版本,不会开天窗。
踩坑实录(本文最值钱的部分)
以下每条都是我实际踩过的,按「现象 → 原因 → 解法」记录。
1. 创建项目时选了 Pages,部署直接报错
现象:Workers-specific command in a Pages project。
原因:本站是 SSR(产物含 Worker 运行时),且部署命令是 npx wrangler deploy,这是 Workers 的部署方式;Pages 项目的构建模型不兼容。
解法:创建时选 Workers → Connect to Git,别选 Pages。
2. 全站 500,日志提到 Node 内建模块
原因:缺 nodejs_compat 兼容标志,SSR 用到的 Node 能力在 Worker 运行时不可用。
解法:wrangler.toml 里带上 compatibility_flags = ["nodejs_compat"](仓库已带)。
3. sitemap / RSS 里是错域名
现象:产物里出现 your-domain.com 占位符,或线上 canonical 指向 example.com。
原因:PUBLIC_SITE_URL 没设;更隐蔽的一层——Astro 不会把 .env 注入 astro.config.mjs 加载期的 process.env,只有 import.meta.env 能拿到,所以配置文件里直接读 process.env.PUBLIC_SITE_URL 会静默拿到空值,回退默认站点地址。
解法:astro.config.mjs 改用 Vite 的 loadEnv 显式读文件环境变量,同时兼容 CI 里 process.env 的构建变量,两条路都兜住;改完变量 Retry deployment。
4. 后台发布文章报错(缺 author 列)
原因:文章表早期没有 author 列,后台发布逻辑写库时必填。
解法:执行 supabase/posts_author.sql 补列。这就是第二步强调「五个 SQL 文件一个都不能少」的原因——少任何一个,后台就有对应功能会报错。
5. 后台改的环境变量「神秘回滚」
现象:在 Cloudflare 后台把某个配置变量的值改掉,过一段时间又变回旧值。
原因:部署命令是 npx wrangler deploy,而 wrangler.toml 的 [vars] 会随每次部署同步上线——后台改的普通变量可能被仓库里的值覆盖(Secret 类型不受影响,所以现象常常是「公开变量回滚了、密钥没变」的诡异组合)。
解法:仓库 wrangler.toml 与后台变量保持一致。我最终把 toml 改成和后台相同的值,两边对齐,从此没有任何一方能悄悄覆盖另一方。改动配置后记得 Retry deployment 才会生效。
6. 国内访问后台要「看网络脸色」
原因:后台页面在浏览器里直连 Supabase(*.supabase.co),国内直连不稳定;但读者侧走的是 Cloudflare 边缘,完全不受影响。
解法:发文时用顺畅的网络登录后台即可,读者无感知。这也是架构设计的结果——读写路径分离,读者侧不依赖 Supabase 可达性。
7. Workers 免费额度用不用操心?
实测口径:构建 1–3 分钟/次,3000 分钟/月对个人站等于用不完;运行时 10 万请求/天、单次 CPU 10ms,SSR 渲染通常远低于 10ms。唯一要注意的是「改代码才构建」——发文不消耗构建分钟。
费用清单
| 项目 | 费用 |
|---|---|
| 域名 | 唯一历史支出(早年购买,按年续费) |
| Cloudflare Workers / DNS / HTTPS | 0 |
| Supabase(数据库+鉴权+图床) | 0(免费档) |
| GitHub | 0(公开仓库) |
| 合计月成本 | 0 元 |
日常维护
- 发文/改文章:
/admin后台操作,即时生效,零部署。 - 改代码/样式/功能:本地改 → push → CF 自动构建(1–3 分钟)→ 完成。
- 改配置(环境变量、构建参数):Cloudflare 后台改 → Retry deployment。
- 数据备份:Supabase 后台随时可导出;文章本身是 Markdown 文本,库在手,天下我有。
- 证书与续期:Cloudflare 自动,不用管。
结语
从 2018 年那台需要手动运维的阿里云服务器,到 2026 年这套「推送即部署、发文即上线」的全托管组合,变化的不只是技术栈,更是拥有一个个人网站所需的门槛——它从「得是个运维」,降到了「有个 GitHub 账号就行」。而这次重建全程是借 AI 之力完成的:我对 Supabase 并不精通,但我知道该问什么问题。
如果你照着这篇文章也搭起了一个,欢迎来评论区还愿。
一如既往,一以贯之。
评论0
还没有评论,来抢沙发吧。