
给一个「没有后端」的博客,配一个后台
你现在看到的这个博客,就是这篇文章要讲的东西——它自己是怎么搭起来的。
想要的东西,有点矛盾
我想要一个博客,同时满足几个看起来有点打架的要求:
- 静态:没有服务端、没有数据库,没东西可被黑,托管还免费;
- 内容我自己掌控:正文都是纯文本、带版本历史,哪天平台没了也能整包搬走;
- 但又要有个「后台」:能像写公众号那样在网页里点点写写,不必每次都开终端敲命令。
前两条指向同一个答案:静态站 + git + GitHub Pages。第三条,才是这篇的主角。
骨架:Astro + GitHub Pages
正文用 Markdown 写,Astro 负责把它们编译成静态 HTML(顺带自带 RSS、站点地图、图片优化)。仓库推到 GitHub,一个 GitHub Actions 工作流自动构建、部署到 GitHub Pages,绑上自己的域名。改动 push 上去,大约一分钟就上线。
这一层的好处很实在:读者访问的是 GitHub 的服务器,我这边一台服务器都不用开,也没有任何入站端口暴露。
「静态博客没有后台」——对,也不对
静态站确实没有传统后台:没有登录面板,没有数据库。但「编辑内容」这件事,其实可以放在三个地方:
- 直接改 Markdown,然后
git push(我甚至让 AI 帮我写、帮我提交); - GitHub 自带的网页版 VS Code——在仓库页按一下
.键就开; - 一个跑在浏览器里的可视化 CMS。
前两个我一直在用。但我还想要第三个——于是有了下面这段折腾。
给静态站装一个 CMS:卡在「一个 secret」上
我选了 Sveltia CMS:它本身也是一堆静态文件,丢进仓库的 /admin 目录,就成了博客上的一个可视化编辑器。你在里面写文章、点保存,它通过 GitHub 的 API 把改动直接 commit 回仓库——又触发一次自动部署。整个过程,服务端依然是零。
只有一个地方卡住:用 GitHub 登录需要走 OAuth,而 OAuth 要用到一个 client secret;静态页面存不住 secret(放前端等于公开)。所以你需要一个很小的「中转」,替静态页握着这个 secret、完成和 GitHub 的握手。
这个中转放哪,有两个选择:
- Cloudflare Worker:serverless、免费、省心;
- 自己的服务器:完全掌控。
我选了后者,扔在自己一台小 VPS 上。为了不折腾,我没有新开域名和证书——直接复用了那台机器上另一个已有站点的 nginx 和 HTTPS 证书,只在它身上额外「劫持」了 /auth 和 /callback 两个路径,转发给一个几十行的小程序。这个小程序做的事很简单:拿着 secret 替静态页跟 GitHub 换到登录令牌,再把令牌传回浏览器里的 CMS。
踩了个小坑记一下:这台 VPS 用的是面板管理的 nginx,改完配置
nginx -s reload居然不生效,必须systemctl restart nginx才认。排查了好一会儿才反应过来。
撞见一点 SSO 的小心思
配到一半我忽然反应过来:GitHub 登录,本质不就是一种 SSO 吗?
我没有自己存密码、没有自己管一套账号体系,只是把「你是谁」这件事,委托给了一个我信任的第三方。顺着想下去还有更妙的:像 Cloudflare Access 这类「身份感知代理」,可以用同一个 GitHub 身份,守在我一堆自建服务的最前面——一次登录,通吃所有后台。
把「身份」统一外包,把「服务」各自自建,这两件事其实不冲突,反而很配。
最后:我喜欢它「散」
我最喜欢这套结构的一点,是它足够松散:
- 内容在 git 里(本地、GitHub、任何 clone 过的机器上都有副本);
- 托管在 GitHub Pages(免费、稳、不归我维护);
- 编辑器/后台在我自己的 VPS 上。
三者互相独立。VPS 哪天炸了,博客照常在线,我大不了回去用 git 发文;GitHub 哪天抽风,本地 clone 还在,换个地方 push 就能复活。没有哪一个零件的死亡是致命的。
这大概也是我做很多东西时的老毛病:总想多留一条路。
(本文涉及的域名、密钥、主机名均已脱敏。)