32 年后,有人用 C 和 CGI 写了个现代 Web 应用
1994 年想做动态网站,流程是固定的:写个 C 程序,扔进 cgi bin 目录,让 Apache 每个请求 fork 一次。三十二年后,独立开发者 Corey Prophitt 用基本同样的技术,交付了一个真正的商业产品:marketyour.games,给内容创作者做的搜索后台,有用户账号、文件夹管理、Strip

1994 年想做动态网站,流程是固定的:写个 C 程序,扔进 cgi-bin 目录,让 Apache 每个请求 fork 一次。三十二年后,独立开发者 Corey Prophitt 用基本同样的技术,交付了一个真正的商业产品:marketyour.games,给内容创作者做的搜索后台,有用户账号、文件夹管理、Stripe 支付、Postmark 事务邮件,全站渐进增强,一行 JS 都不依赖。
他的技术栈缩写叫 BCHS:OpenBSD、C、httpd、SQLite。没有框架,没有模板语言(HTML 直接用 C 输出),没有独立的数据库进程(SQLite 直接打开文件),静态链接,一个二进制文件扔进目录就算部署。文章在 Hacker News 拿下 600 多赞、270 多条评论,评论区一半致敬、一半质疑:为什么不用 Go 或 Rust?字符串处理不痛苦吗?并发写怎么办?我请来老朋友周砚——写了十多年 C 的后端基础设施工程师,现在一家云厂商做高性能网关——聊聊这件事到底值多少钱。
本文由每日自动管线生成:选题与素材来自 Hacker News 公开讨论,访谈内容由 AI 基于原文整理,嘉宾为角色设定,观点不代表任何真实人物。
小听: 周砚,先问你的第一反应。看到"2026 年用 C 写 Web 应用还跑 CGI"这个标题,你是觉得致敬,还是觉得行为艺术?
周砚: 第一反应是羡慕,第二反应是:这人说的是实话。他开篇那句很关键——CGI 在 2004 年衰落是正当的,但在 2026 年,那些理由很多已经不成立了。当年 CGI 被抛弃,一个核心原因是 fork 进程太贵;现在内核对 fork 的优化、写时复制,早就不是当年的开销了。技术债的利息会变,但很多人还按 2004 年的利率在还。
小听: 他最打动我的一句是架构观:现代 Web 应用通常是 nginx 在前,反向代理到另一个语言写的服务,"本质上有两个 Web 服务器,其中一个还顺带管业务逻辑"。CGI 是让一个 Web 服务器直接调用你的业务逻辑。
周砚: 这句是全文的题眼。职责划分一下子干净了:httpd 管 HTTP,你的程序管业务,谁也别越界。调试体验更是降维打击——你的应用就是个普通程序,shell 里管道喂它一个请求,看 stderr,gdb 单步,全是 Unix 老手艺。他说 CGI 像 serverless,"serverless minus the vendor",我觉得这是全篇最准的比喻:监督者按请求调用你的代码,给一袋环境变量,收走结果——没有厂商账单惊吓版。
小听: 部署也确实离谱地简单:替换一个文件就是发布,不用重启,没有停机,新请求自然拿到新二进制。单二进制、SQLite 文件、三个依赖,"整个东西能装进脑子里"。
周砚: "能装进脑子里"是工程师能给一个系统的最高评价。我们现在的问题恰恰相反:一个中等项目,依赖图里有几百个间接依赖,每个都由你没听说过的人维护,发布凭证可能是 2015 年复用的密码。在每周都有供应链攻击、包仓库被投毒的今天,他说"我的供应链是一个 C 编译器加三个库",这句话的含金量被严重低估了。想攻破他的供应链,得先攻破 libcurl——那是全互联网盯着的项目,不是周下载量 11 次、维护者 2019 年就跑路的包。

装得进脑子的系统,才谈得上真正拥有。
小听: 评论区最高赞的质疑也很直接:手写 C 项目很好,但同样的好处 Go、Rust 也有,还附赠内存安全。为什么偏偏是 C?
周砚: 这条问到了点子上,作者自己其实也没完全答上来。他说选 C 的部分原因是"没有第二种语言让编辑器半吊子识别",写 Go 模板时 vim 高亮打架、helix 识别混乱、Rust 的 askama 让 rustrover 认不出 jinja——用 C 写 HTML,反而没有语法高亮的内战。这话听着像段子,但做过模板工程的人都懂那种疼。不过说实话,内存安全这关他是绕过去的:OpenBSD 的 pledge 和 unveil、httpd 默认 chroot 沙箱,这些都是在用操作系统层面的纵深防御,补语言层面的短板。这条路走得通,但门槛是"你得真懂 Unix",这本身就是一种筛选。
小听: 另一条技术质疑:每个请求一个进程,SQLite 允许多个进程同时写吗?
周砚: 问得好。SQLite 的并发模型是读多写串行,写的时候是库级锁。对 marketyour.games 这种量级的产品完全够用,但这也是 CGI + SQLite 组合的天花板所在——它天然适合中小规模、读多写少的应用。作者自己也留了后路:如果 fork 真成了瓶颈,FastCGI 是"足够简单的升级"。这才是成熟工程师的表达:不宣称银弹,把边界条件摆出来。
小听: 他自己承认的最大短板是"什么都得自己写":cookie 解析、查询参数校验、表单解析、输出转义,全靠一个自研的 cgi.h 头文件。他说想把它开源成 stb 风格的单文件库。
周砚: 这是整篇文章最诚实的一段。CGI 是接口不是框架,开箱即用的东西极少。评论区有人贴了 kcgi、cgi-lib 这些现成的库,但他说"用库等于买下它的 API 和安全模型",我特别理解——安全敏感的基础设施,依赖别人的抽象就是把命门交出去。他想做 stb 式的 drop-in 工具集,这个方向是对的:生态缺的不是又一个框架,是可审查、可复制粘贴的小工具。
小听: 还有个细节我很喜欢:他是个极简主义者,Vim 零插件,不用 LSP、不用补全、不用 AI。他说复杂的技术栈才需要复杂的工具去"蹚过层层抽象",而他的栈简单到"打开文件,读完,就知道它在干嘛"。
周砚: 这句话对现在的 AI 编程时代是个提醒。我们天天讨论 agent 怎么理解十万行代码的 monorepo,却很少问:为什么它需要理解十万行?当系统简单到人能装进脑子,工具链自然就退化成可选项——sanitizer、valgrind、gdb 这些"老年"工具,用了几十年,依然够用。简单性不是怀旧,是架构选择。

工具越老,越说明问题早就被想明白了。
小听: 最后一个问题,替读者问的:看完这篇,我明天要不要用 C 重写我的后台?
周砚: 千万别——除非你真懂自己在干嘛。这篇文章的价值不是"大家都去写 CGI",而是它逼你回答一个问题:你的技术栈里,有多少复杂度是必要的,有多少是惯性?CGI 的全部接口就是几个 HTTP 基本概念,"懂 HTTP 就懂 CGI",学一次终身有效,这种知识的半衰期是按十年算的。而框架的半衰期是按季度算的。下次你 npm install 拉进来 400 个包的时候,想起这篇文章,就够了。
这篇文章最有意思的地方,是它把"简单"从一种审美变成了可审计的工程属性:依赖可数、部署原子、调试直给、知识不过期。至于评论区那句"页面文字都看不清也算现代",我倒觉得是误伤——作者的审美和他的架构选择是两回事,别因为 CSS 吵架,错过了真正值得吵的东西:我们到底需要多少 moving parts,才能跑一个网站?本文观点仅代表访谈角色设定,原始讨论见参考链接。