Hacker News Best

A Modern Web App in C

6.9内容质量

TL;DR · AI 摘要

A modern web app in c Corey Prophitt INTRO If you wanted a dynamic website in 1994, you wrote a C program, dropped it in...

核心要点

  • 主题聚焦:A Modern Web App in C
  • 来源:Hacker News Best,建议结合原文判断细节。
  • AI 分析暂不可用,本条为保底评分与摘要。
#AI#编程#前端#后端#云计算
打开原文

用 C 语言构建现代 Web 应用 | Corey Prophitt

简介

1994 年如果你想构建动态网站,你会编写一个 C 程序,将其放入 cgi-bin 目录,让 Apache 按请求 fork 进程执行。32 年后,我使用本质上相同的技术发布了商业 Web 产品。

marketyour.games 的所有功能都符合现代标准。它是一个包含用户账户、内容创作者搜索仪表板、整理文件夹、通过 Stripe 支付和通过 Postmark 发送事务邮件的 Web 产品。它遵循渐进增强原则,完全不需要 JavaScript。

唯一不现代的是它的实现方式和交付方式。以下是我关于 2026 年用 C 语言开发真实产品并通过 CGI 提供服务的思考。

TLDR

CGI 的衰退在 2004 年是合理的,但在 2026 年已不再成立。

你完全可以用 C 语言开发现代 Web 应用。我做到了。一个二进制文件、一个 SQLite 数据库文件、三个依赖项,整个项目可以装进你的大脑。

实现方式

技术栈是 BCHS:OpenBSD、C、httpd 和 SQLite。

没有框架。你编写一个 C 程序,读取 CGI 环境变量并响应请求。

没有模板语言。你直接用 C 生成 HTML。

没有独立的数据库服务器。通过 SQLite 的 C API 直接打开数据库文件。

没有容器。构建静态链接的二进制文件,直接放入目录即可。

每个请求都会生成新进程。部署新版本只需替换一个文件。这意味着无需重启服务器,没有停机时间。新请求会自动使用新二进制文件。

OpenBSD 提供合理的默认配置,并提供 pledge 和 unveil 等工具,让你能够真正安全地编写 C 程序。httpd 默认使用 chroot,因此 CGI 程序会在文件系统的独立区域运行。

我依赖三个额外库:

libcurl 处理所有发往 Stripe 和 Postmark 的出站请求。

libsodium 处理随机标识符生成、密码哈希和 Stripe Webhook 的 HMAC 验证。

libcjson 处理 Stripe 和 Postmark 的 JSON 解析。

其中只有两个是实际构建依赖项:libcurl 和 libsodium。

libcjson 是一个单独的 C 文件,没有外部依赖。SQLite 提供官方合并构建(单个 C 文件)。

这两个库都作为项目的一部分进行打包和构建。

最终结果是一个几乎没有移动部件的构建系统。干净构建只需几秒钟,重新构建可在半秒内完成。

而不是传统方式提供静态资源,我将所有流量从 httpd 路由到 CGI 程序。每个资源都编译进可执行文件,直接从内存提供服务。

CGI 为你提供一个直接调用业务逻辑的单一 Web 服务器。职责划分清晰明确。

它也让调试变得更简单。你的应用程序就是一个普通程序,可以直接在终端运行:通过管道传入请求,查看 stderr 输出,用 gdb 单步执行。

从某种意义上说,CGI 与无服务器架构类似:监督者根据每个请求调用你的代码,传递一个环境变量包并收集结果。CGI 是无服务器架构,只是少了供应商(以及令人意外的账单)。

供应链风险极低。

随着自主代理的兴起和每周对包仓库的攻击,拥有极少数依赖项让人感到安心。

大多数现代技术栈都附带包管理器和配套的生态系统。这虽然方便,但依赖关系图会迅速膨胀。一两个直接依赖可能引入数百个间接依赖,每个都由你从未听说过的开发者维护,而发布凭证可能使用了自2015年以来重复使用的密码。

我的供应链仅包含一个 C 编译器和三个库。要入侵它,必须入侵 libcurl... 这是一个整个互联网都在关注的项目,而不是一个每周仅有11次下载、所有者已于2019年离开的包。

工具链简单直观。

我本质上是个极简主义者。我使用零插件的经典 Vim。这意味着没有 LSP、没有自动补全、没有代码格式化工具,也没有 AI。

复杂的科技栈需要复杂的工具链:需要语言服务器来处理多层抽象。但这个技术栈足够简单,普通编辑器就已足够。整个应用程序只是一个包含 C 文件的目录和一个 Makefile。

打开文件,阅读它,你就能立刻明白它的作用。

优秀的工具链触手可及。

当你确实需要工具时,它们就在附近。Sanitizers、valgrind、gdb、ctags、cscope... 这些是经过数十年完善(或者说老化)的成熟工具,只要系统能编译 C,它们都只需一个包安装即可。

这种成熟度不仅体现在工具本身:许多编辑器都内置了集成支持。Vim 本身就原生支持 ctags、cscope,并自带图形化 gdb 前端(参见 :h Termdebug)。

用 C 编写 HTML 和 CSS 并不难。

我起初对用 C 编写整个前端持犹豫态度,但实际操作远没有想象中痛苦。

我曾多次遇到这些情况:

与 Vim 较劲以高亮 Go 的 html/template 语法。与 Helix 较劲以检测 Go 模板而不破坏 HTML 检测。与 Rustrover 较劲以识别使用 askama 的项目中的 Jinja 模板。

使用 C 意味着编辑器无需处理第二种语言的半检测,没有混合语法导致高亮失效,也无需插件协调两种语言的协作。

单纯编写 C 代码令人耳目一新。它并不美观,但 Tailwind 也不美观。

SQLite 是一股清新的空气。

我过去总是默认使用 Postgres 作为 Web 应用的数据库。它确实是个优秀的数据库,但需要承担一些运维负担。需要照看服务器进程,频繁升级版本。

SQLite 完全没有这些麻烦。没有进程、没有配置、没有依赖。你的应用程序直接打开数据库文件。运维负担微乎其微。

我自行实现了一个,仅用了一个头文件(cgi.h),但我不确定这是否是最佳解决方案。虽然存在像kcgi这样的库,但采用它们意味着需要接受其API和安全模型。

我希望看到一套共享工具库,开发者可以直接使用(就像stb库那样)。

也许我会先发布cgi.h作为起点。

结论

CGI虽然古老,但并未被遗忘。

我们在2000年代初放弃它的大部分原因在今天已不再成立。进程forking的开销远不如从前,现代内核对此进行了大量优化。

如果forking的开销 ever 成为问题,升级到FastCGI就是一个简单的解决方案。

用C语言编写现代Web应用完全可行——marketyour.games就是一个商业产品,它运行在单个二进制文件上,使用的是2004年正式标准化的服务器模型。

它比我之前部署过的任何系统都更简洁。我将会再次使用这个技术栈。