FastCGI 规范发布于 1996 年。30 年后,它又被拿出来谈,不是因为老协议突然翻红,而是因为 HTTP 反向代理还在反复踩同一类坑。

最近的例子是 Discord media proxy 被披露 desync 漏洞,研究者称可导致私有附件被窥探。这个案子不能推导出“所有 HTTP 反代都危险”。它只能说明一件事:代理和后端只要对同一段 HTTP/1.1 报文边界理解不一致,入口就出现了。

这篇原作者式观点文章真正有意思的地方,也不在“FastCGI 要取代 HTTP”。它说的是更窄的一段:反向代理到后端服务器之间,为什么一定要继续用 HTTP?

FastCGI 不是 CGI 进程模型,而是一条后端线协议

很多人听到 FastCGI,会先想到 CGI、.cgi、服务器拉进程、上古 Web。这个反应很正常,但也容易误判。

FastCGI 在这里讨论的重点,是代理和后端之间的线协议。代理可以通过 TCP 或 UNIX socket 把请求发给一个长期运行的后端进程。后端不是每个请求启动一次。

以 Go 为例,标准库里有 net/http/fcgi。理论上,应用可以把 http.Serve 换成 fcgi.Serve,handler 仍然用熟悉的 http.ResponseWriterhttp.Request

主流代理也不是完全不支持。Apache、Caddy、nginx、HAProxy 都能接 FastCGI backend。nginx 很早就支持 FastCGI 后端,而 HTTP/2 backend 支持到 2025 年末才出现;Apache 的 HTTP/2 backend 支持仍标为 experimental。

几个差别压缩看:

维度HTTP 反代到后端FastCGI 反代到后端
报文边界HTTP/1.1 依赖头部描述长度,容易出现解析分歧结构化分帧,边界更明确
可信信息常塞进 X-Real-IPX-Forwarded-ForTrue-Client-IP 等头REMOTE_ADDR 等参数,与客户端头域隔离
调试工具curl、抓包、框架生态更顺工具弱,curl 不支持直连 FastCGI
能力限制WebSocket 等路径成熟不支持 WebSocket,部分路径优化不足
适用位置浏览器到服务器、服务间调用都常见更适合讨论 proxy-backend 这段窄链路

所以别把它理解成“老协议全面复辟”。它只是提醒:后端链路不一定非要沿用公网 HTTP 的全部包袱。

HTTP 反代最麻烦的两颗雷:边界和可信头

HTTP/1.1 的麻烦在于,它看起来太简单。文本协议,一行行读,人眼也能看懂。

机器最怕这种“差不多”。Content-LengthTransfer-Encoding、重复头、大小写、畸形输入,不同实现只要有一点分歧,代理和后端就可能看到两个不同请求。

代理以为一个请求结束了。后端却以为后面还有半截。request smuggling / HTTP desync 就从这里来。

James Kettle 这些年不断挖出新变体,还提出过“HTTP/1.1 must die”。这话很重,但不是单纯情绪。问题不只是某个 parser 写得差,而是协议给了实现太多解释空间。

HTTP/2 能不能解决这颗雷?能,但条件不轻。代理到后端之间也要一致使用 HTTP/2,明确分帧才真正起作用。只在公网入口用 HTTP/2,后面再降回 HTTP/1.1,坑还在。

另一颗雷更日常:可信信息被塞进普通 HTTP 头。

真实客户端 IP、代理认证后的用户名、mTLS 客户端证书信息,本来应该是代理告诉后端的可信事实。但 HTTP 没有原生的结构化隔离,大家只好发明一堆头:X-Real-IPX-Forwarded-ForTrue-Client-IP

理论上,代理可以先删掉客户端传来的相关头,再写入自己的头。难点在“相关”两个字。

你的应用、框架、中间件、某个库,可能信的是另一个名字。Chi 的真实 IP 逻辑就会优先看 True-Client-IP,再看 X-Real-IP。代理清了一个,后端信了另一个,攻击者就有了空间。

FastCGI 的办法朴素很多。客户端 HTTP 头会加 HTTP_ 前缀;代理提供的可信参数走另一个域,比如 REMOTE_ADDR。这不是靠团队记忆力维持边界,而是协议层先把域分开。

“名不正,则言不顺。”放到基础设施里,就是域不分,信任就乱。

真正受影响的是后端和基础设施团队

这件事和普通用户关系不大。它也不是让所有团队马上迁移 FastCGI。

最该看的是两类人。

角色该做什么不该误读成什么
后端工程师检查应用到底信哪些 IP、认证、TLS 相关头;确认框架和中间件的优先级不要以为代理删了 X-Forwarded-For 就万事大吉
基础设施 / 安全团队梳理 proxy-backend 链路是否仍在使用 HTTP/1.1;评估能否端到端 HTTP/2,或在部分场景试 FastCGI不要把 FastCGI 当成性能必然更好、安全天然无风险的替代品

如果你的后端链路没有 WebSocket,又高度依赖真实 IP、TLS 信息、认证信息,那就别只问“HTTP 配起来方不方便”。要问更硬的一句:哪些字段来自用户,哪些字段来自代理,协议本身有没有帮你分清?

我不太买账的是行业里那种默认答案:统一用 HTTP,省事。

省事是真的。工具好、调试顺、人才多、框架支持成熟。工程团队面对复杂系统,当然会选熟悉路径。

但熟悉感不是安全设计。

把 HTTP 从公网入口一路用到内部后端,短期看很顺。长期看,是把外部输入、代理元信息、应用语义都揉进一个文本锅里。平时没事。一出事,就开始追问:谁解析错了,谁该删头,谁误信了谁。

FastCGI 不性感。名字也吃亏。2026 年还叫 CGI,很像穿旧西装去云原生大会。

可它在反向代理这段链路上有个硬优点:承认边界,承认信任分层,少让后端猜。

当然,代价也要写清。

它不支持 WebSocket。调试工具不如 HTTP。curl 不能直接打 FastCGI。原文也承认,Go 的 FastCGI server 在某些代理和负载下,吞吐可能不如 HTTP/1.1 或 HTTP/2。

这未必说明协议天生慢。更可能是生态优化少。但对工程团队来说,原因没那么重要,结果要算进成本。

接下来真正要观察的,不是 FastCGI 会不会大规模复兴。这个概率不高。

更该看三件事:代理到后端的 HTTP/2 支持会不会成熟;主流框架会不会更严格地区分可信代理字段;安全团队会不会把 request smuggling 和可信头混淆纳入常规架构审查。

铁路、电力、报业、互联网平台都重复过一个旧规律:早期为了扩张,先把接口统一起来;等事故多了,才开始补边界、补治理、补责任。Web 基础设施也不例外,只是这次边界藏在 header 和 parser 里。

FastCGI 30 岁了。它没有变年轻。它只是把一个年轻基础设施的老毛病照出来:我们太相信统一接口,太少追问接口背后的权力边界。