一个绕不过去的矛盾

静态站点最大的优点是**没有服务器**:没有进程要守护,没有数据库要备份,没有依赖要升级,一个月不碰它也不会坏。

但只要你想加一个「登录后才能用的后台」,这个优点立刻变成障碍:

**登录的本质是服务端信任一个凭证。没有服务端,就没有地方建立信任。**

你可以把密码写死在 JS 里——任何人打开控制台都能看到。你也可以用 HTTP Basic Auth——那是托管平台的功能,且无法细粒度控制。你还可以……基本上没有别的办法了。

这就是 Git-based CMS 要解决的问题。

它的解法:把「保存」重新定义为「提交」

传统 CMS 的保存是 `UPDATE posts SET content = ... WHERE id = ...`。

Git-based CMS 的保存是 `git commit`。

这个重新定义是整个架构的支点。它一旦成立,三个难题同时消失:

| 传统 CMS 需要 | Git-based CMS 用什么替代 |

|---|---|

| 数据库存内容 | 仓库里的 Markdown 文件 |

| 服务器渲染页面 | 构建时静态生成 |

| 后台服务器 + 会话管理 | 静态页 + GitHub OAuth |

**内容不再存在数据库里,而是存在版本控制系统里。** 你顺带得到了:完整的修改历史、每一篇文章的 diff、随时回滚、分支上写草稿。

一次保存,到底发生了什么

```

① 你在 /admin/ 写文章,点保存

② CMS(一个纯静态的 SPA)调用 GitHub API

│ 带着你的 access token

③ GitHub 在仓库里创建一个 commit

│ src/posts/2026-09-16-xxx.md

④ 托管平台检测到仓库有新提交

│ 触发构建

⑤ 构建器(Eleventy / Hugo / Astro)渲染出静态 HTML

⑥ 新版本部署上线

```

全流程没有任何一个常驻服务进程。

但 OAuth 需要一次服务端计算

这里有个容易被忽略的细节。上面的第 ② 步需要一个 access token,而拿 token 的流程是这样的:

```

浏览器 ──① 跳转──▶ GitHub 授权页

浏览器 ◀─② 回调─── GitHub(带一个 code)

│ ③ 用 code + client_secret 换 token

??? ← 这一步必须有服务端

```

第 ③ 步**必须携带 `client_secret`**。而 `client_secret` 一旦放进浏览器,就等于公开——任何人都能拿它冒充你的应用。

所以哪怕整个架构都是静态的,**你仍然需要一小块服务端代码**,哪怕只是转发一次请求。

这就是为什么 Cloudflare Pages 的 `functions/` 目录、Netlify 的 Functions、Vercel 的 Edge Functions 会成为标配。它们不是「后端」,只是**几个用来完成 OAuth 握手的无状态函数**:

```js

// functions/callback.js —— 全部服务端代码就是这些

export async function onRequestGet({ request, env }) {

const code = new URL(request.url).searchParams.get('code');

const res = await fetch('https://github.com/login/oauth/access_token', {

method: 'POST',

headers: { 'Content-Type': 'application/json', Accept: 'application/json' },

body: JSON.stringify({

client_id: env.GITHUB_CLIENT_ID,

client_secret: env.GITHUB_CLIENT_SECRET, // ← 只存在于这里

code,

}),

});

const { access_token } = await res.json();

// 把 token 通过 postMessage 交给同源的后台页面

return new Response(callbackPage({ token: access_token, provider: 'github' }), {

headers: { 'Content-Type': 'text/html; charset=utf-8' },

});

}

```

整段代码约 20 行,无状态,不存任何东西。

代价清单

这套架构不是免费的。以下是它的真实代价:

**① 内容在仓库里是明文**

公开仓库意味着:**你写了但没发布的草稿,别人也能在仓库里看到。** 大多数 Git-based CMS 的「草稿」只是在构建时跳过,文件本身照样被提交。

要真正保密,只能用私有仓库,或者把草稿存在别处。

**② 没有即时预览**

传统 CMS 保存后刷新就能看到效果。这里必须等一次构建——通常 1~2 分钟。文章的预览通过本地 `npm run dev` 解决,但这要求你会用命令行。

**③ 没有实时协作**

两个人同时编辑同一篇文章,会撞车。对个人博客无所谓,对团队是硬伤。

**④ 每篇文章都是「一次部署」**

改一个错别字也会触发全站重建。站点大、构建慢的时候,这个摩擦会积累。

**⑤ 搜索、评论、动态查询都得另想办法**

没有数据库,就没有 `WHERE` 子句。搜索要用构建时生成索引的方案(如 Pagefind),评论要挂第三方(如基于 GitHub Discussions 的 Giscus)。架构上说得通,但确实是额外工作。

那该选哪个

| 方案 | 适合 | 不适合 |

|---|---|---|

| **Git-based CMS** | 个人博客、技术文档、重视版本与可迁移性 | 团队协作、需要即时预览、内容要保密 |

| **Headless CMS**
(Contentful / Sanity) | 内容规模大、要对接多端、要精细的编辑权限 | 不想依赖第三方、想完全掌控数据 |

| **WordPress** | 需要插件生态、非技术编辑者多 | 不想维护服务器和数据库 |

| **纯手写 Markdown** | 只要几十篇文章、不在意可视化编辑 | 写作与发布流程想一体化 |

我的判断标准很简单:

**如果「内容」和「代码」本来就在同一个仓库里,Git-based CMS 是顺理成章的。

如果内容需要独立于代码演进,那它就不该住在 Git 里。**

最后

这套架构最迷人的地方,是它**没有为了「后台」而引入一整个运行时**。

你付出的是一小块无状态的 OAuth 转发代码,换回来的是:没有数据库、没有服务器进程、完整的版本历史、以及随时能把整个站点复制到任何静态托管平台的能力。

在「够用」和「简单」之间,它划出的那条线,我觉得挺漂亮。