不少人对「自己搭博客」的印象停留在两选一:

  • 要么上 WordPress / Typecho 这种动态博客 —— 得租一台服务器,年年掏钱,还要操心备份和安全;
  • 要么用 Hexo / Hugo 这种静态博客 —— 托管免费,但只能本地写 Markdown、敲命令发布,换台电脑就抓瞎,写作体验像在做运维。

我最后的方案是:用静态站的成本,做出「动态站」的体验。

  • 打开网页登录后台,在 Notion 风格的编辑器里写文章,截图直接粘贴上传;
  • 点一下「发布」,一分钟后文章就在线上出现;
  • 图床、评论、搜索、RSS 全都有;
  • 全程 0 元——没有服务器,没有付费订阅。

这篇文章把我这套东西从零到一怎么搭起来的讲清楚,希望你能照着复刻一个。


一、总体架构

先上全景图,后面每一步都是在填这张图的一块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
             ┌────────────────────────────────────────────┐
│ Cloudflare Pages │
git push ──▶│ hexo generate → 静态文件 → 全球 CDN 边缘节点 │──▶ 访客
│ │
│ Pages Functions(免费送的后端): │
│ /admin 在线后台(密码登录) │
│ /api/* 发布文章 / 上传图片 / 管理图床 │
└───────┬──────────────────────┬─────────────┘
│ GitHub API 写文件 │ 图片存取
▼ ▼
┌────────────────┐ ┌──────────────┐
│ GitHub 私有仓库 │ │ Cloudflare R2 │──▶ blogimg.xx 域名
│ (源码 = 唯一事实) │ │ (图床, 10GB) │ 返回图片外链
└────────────────┘ └──────────────┘

核心思路就三句话:

  1. Hexo 负责把 Markdown 变成静态 HTML,本地不需要常驻任何东西;
  2. GitHub 私有仓库存放源码,它是唯一事实来源——后台发布文章,本质上也是往这个仓库写文件;
  3. Cloudflare 免费托管 + 免费后端:Pages 负责静态文件和自动构建,Pages Functions 提供动态接口,R2 当图床。

二、成本清单:到底花了多少钱

组件 用的什么 花费
静态站点生成器 Hexo + Butterfly 主题 ¥0(开源)
代码托管 GitHub 私有仓库 ¥0(Free 套餐够用)
托管 + CDN + HTTPS Cloudflare Pages ¥0(免费档)
动态后端 Cloudflare Pages Functions ¥0(免费档,每天 10 万次请求)
图床 Cloudflare R2 ¥0(免费 10 GB,流量不收费)
域名 dpdns.org 免费二级域名 ¥0
评论系统 giscus(GitHub Discussions) ¥0

唯一可能产生的花费:你想要一个自己买的域名(一年几十块)。我用的 dpdns.org 免费域名,暂时一分钱没花。

免费额度都是「对个人博客严重过剩」级别:Pages 每月 500 次构建、R2 存 10 GB 图片、Functions 每天 10 万次请求。具体数字以 Cloudflare 官网为准,但对你我这种量级的博客,放心用。


三、第一步:本地把 Hexo 站跑起来

这步网上教程最多,我只说三个容易踩的点。

1. 主题用 submodule 管理,永远别直接改主题文件。

1
git submodule add https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly

Butterfly 的所有定制都写在站点根目录的 _config.butterfly.yml 覆盖文件里。这样以后主题升级只是更新一下 submodule 指针,你的定制一行都不会丢。我后来把主题 fork 了一份做深度二开,也只是把 submodule 指向自己的 fork,原则不变。

2. 配色之类的 Stylus 配置改完要 hexo clean。

这类配置会被编译缓存进 db.json,不 clean 你会遇到「配置明明改了颜色就是不变」,怀疑人生的经典来源。

3. _posts 里的文件名就是永久链接的一部分。

本站永久链接格式是 :year/:month/:day/:title/,文件名里的中文会直接出现在 URL 里。介意外文中文 URL 的话,可以用 hexo-permalink-slug 之类的插件,或者文件名用英文、标题写中文。

四、第二步:推上 GitHub,让 Cloudflare 帮你构建

  1. 在 GitHub 建一个私有仓库,把整个 Hexo 站推上去。私有仓 + 免费额度完全够,草稿和没公开的折腾不怕被人看到。
  2. 到 Cloudflare 控制台 → Workers & Pages → 创建 Pages 项目 → 连接这个 GitHub 仓库。
  3. 构建配置只需要两行:
1
2
构建命令:  npm run build        # 即 hexo generate
输出目录: public

之后每次 git push,Cloudflare 自动拉代码、装依赖、构建、发布到全球 CDN,全程一分钟以内。HTTPS 证书自动配好,什么都不用管。

域名:Pages 项目默认送一个 *.pages.dev 域名,嫌长的话可以在 Cloudflare 里绑自定义域。我是用 DigitalPlat 的免费域名服务(dpdns.org)申请的二级域名,解析托管在 Cloudflare,也是免费。

到这一步,你已经拥有一个免费、全球加速、自动部署的静态博客了。接下来的部分才是「别人不知道」的。

五、第三步:图床——Cloudflare R2

为什么需要图床? 文章里的截图如果直接放仓库里,仓库会越来越肥,构建越来越慢,而且 Markdown 里的相对路径离开本地就失效。正确姿势是图片放对象存储,Markdown 里只写外链。

市面上免费图床不少(imgbb、sm.ms……),但普遍的问题是:域名不可控(哪天挂了或换域名,全站图片跟着遭殃)、额度小、不知道哪天就政策收缩。所以我选了 Cloudflare R2:

  • 免费 10 GB 存储,流出流量免费(这点是 R2 最大的杀手锏,别的云对象存储流量费是大头);
  • 可以绑自己的域名,图片链接永远在自己的域名下,可控;
  • 和 Pages 同在 Cloudflare,后台接口直接用 env.IMAGES_BUCKET 访问,不用签什么临时凭证。

R2 开通后:创建一个桶 → 在设置里绑定一个自定义域(比如我的 blogimg.kkkkikun.dpdns.org)→ 桶里的对象就能通过这个域名直接访问了。

我的上传接口做的事很简单:收图片 → 校验类型 → 按 images/年/月/时间戳-文件名 存进桶 → 返回外链。有两个细节值得抄:

  • 只收 png/jpeg/gif/webp,拒绝 SVG。SVG 能内嵌脚本,允许上传等于在图床域名上开了一个存储型 XSS 的口子,而图床域和主站同属一个大站范围,不能不防;
  • 按年/月分目录,后台提供图片列表和删除,清理过期截图不用进控制台。

写作体验上是:后台编辑器里 Ctrl+V 粘贴截图 → 自动传 R2 → 编辑器里插入外链。发图文文章从此不需要「先开图床网站 → 上传 → 复制链接 → 粘贴」四连。

六、第四步:在线发布后台(本文最有意思的部分)

很多人以为静态博客注定「只能本地发布」。其实 Cloudflare Pages 自带一个容易被忽略的能力:Pages Functions——在仓库里放一个 functions/ 目录,它就是一个跑在 Cloudflare 边缘的 serverless 后端,免费额度每天 10 万次请求,对个人后台绰绰有余。

于是静态站就有了后端。我的后台是这么工作的:

1. 登录与鉴权。

后台就一个口令,存在 Cloudflare Pages 的环境变量里(不进代码仓库)。登录成功后发一个 SHA-256 派生的会话 cookie,有效期 7 天;/api/* 用一个中间件统一校验,所有接口不用各自写鉴权。口令未配置时所有接口直接锁死——安全默认,防止裸奔。

2. 发布文章的本质:往 GitHub 仓库写一个文件。

这是整套设计里我最得意的一招。后台点「发布」,Function 做的事是:

1
2
3
4
5
6
7
组装 front matter(title/date/tags/categories/cover/description)
↓
调 GitHub Contents API,把 Markdown 写入 source/_posts/
↓
触发 Pages 的 Deploy Hook(一个 webhook)
↓
Cloudflare 自动重新构建,约一分钟后文章上线

也就是说后台不需要任何数据库——GitHub 仓库就是数据库,git 历史就是文章的修改记录,永远可回滚。文章的增删改查全部是对同一个 GitHub API 的封装,我还做了草稿目录(_drafts/):写一半的文章存草稿,不触发发布,哪天写完一键转正。

这里有个我真实踩过的坑:生成文章日期别用 toISOString()。它是 UTC 时间,早上八点前发文,日期会被算成前一天,permalink 直接错位。要用 toLocaleString 转成上海时区再格式化。

3. 编辑器。

后台编辑器用的是 Milkdown Crepe,Notion 风格的所见即所得 Markdown 编辑器,用 esbuild 打包成自包含的单文件产物随仓库提交,部署时不需要额外构建步骤。粘贴图片自动走上面的 R2 上传接口。

到这一步,写作体验已经和掘金/知乎这类在线平台没差别了:打开网页 → 登录 → 写 → 发布。但它发出来的是你自己域名下的、纯静态的、没有广告和你完全掌控数据的文章。

七、第五步:评论、搜索、RSS 这些「动态功能」

剩下的动态能力,全都有成熟的免费方案:

  • 评论:giscus。 借 GitHub Discussions 当评论存储,访客用 GitHub 账号登录即可评论,零服务器、零成本,数据还归你自己(我单独建了个公开仓库存评论,和源码仓解耦)。国内访客加载可能略慢,开 lazyload 后不影响首屏。
  • 站内搜索:hexo-generator-searchdb。 构建时生成一个索引 JSON,前端纯静态查询,不需要任何搜索服务。
  • RSS:hexo-generator-feed;sitemap:hexo-generator-sitemap。都是装上就完事的插件,分别喂给 RSS 阅读器和搜索引擎。

八、这套东西的边界(诚实说)

免费方案不是没有代价,有几个点你要心里有数:

  1. 免费额度是「条款」不是「合同」。Cloudflare 免费档对个人博客完全够用,但如果哪天某篇文章爆了,构建次数、请求量都要留个心眼;
  2. 后台是给「自己」用的,不是给「很多人」用的。它没有多用户、没有审批流,防的是陌生人和脚本,不是恶意 insider;
  3. 域名是软肋。免费域名理论上有政策风险,重要内容记得定期 git pull 留本地备份——反正源码在 git 里,重建整个博客也就十分钟。

九、总结

你以为需要 实际需要
一台服务器 不需要
数据库 不需要
图床会员 不需要
每年几百块 0 元
本地写稿 + 命令行发布 打开网页,写完点发布

整套东西的技术关键词就是:Hexo + GitHub 私有仓库 + Cloudflare Pages / Functions / R2 + giscus。没有什么高深的东西,更多是「知道这些免费件可以这么拼起来」——希望这篇能帮你把拼图凑齐。

如果你照着搭的过程中卡在哪一步,或者想看我某个环节的具体实现,欢迎在评论区(GitHub Discussions)留言交流。