阅读提要
协议升级主要在边缘完成,后端多数时候无感。但有三件事会直接影响你的实现:请求头变大、连接复用、以及「客户端 IP」取不到。
一句话概括演进
HTTP/1.1 的问题是队头阻塞:一个连接同时只能处理一个请求,前一个慢了,后面的全等。
HTTP/2 用多路复用解决:一个 TCP 连接上并发多个流。但 TCP 层仍然有队头阻塞——一个包丢了,整条连接的所有流都要等重传。
HTTP/3 换成 QUIC(基于 UDP),把「流」下沉到传输层,单个流丢包不影响其他流。
对后端开发者,这三层的差别大部分被反向代理吃掉了。但有几处会漏到你这里。

影响一:请求头压缩改变了「头部开销」的直觉
HTTP/1.1 里,一个请求带几百字节的头很常见。HTTP/2 引入 HPACK 压缩,重复的头字段只在首帧传一次。
结果是:过去为了省头部而做的设计,现在没必要了。比如把多个资源合并成一个大文件(雪碧图、CSS 合并),在 HTTP/2 下收益大幅下降,反而破坏了缓存粒度——改一个图标要重新下载整张图。
对后端的具体影响:不要为了减少请求数而设计「批量接口」,除非业务上确实需要原子性。合并请求会让缓存失效、错误处理变复杂,收益却很有限。
影响二:连接复用让「每请求一连接」的假设失效
HTTP/1.1 时代,一个 TCP 连接对应一个请求(或一个短时间的 keep-alive)。有些后端逻辑依赖这个假设——比如「连接建立时初始化上下文」。
HTTP/2 下,一个连接承载几百个请求,连接可能保持几分钟。任何绑在连接上的状态(会话、事务、临时变量)都会串味。
// 危险:把状态挂在连接上
server.on('connection', (socket) => {
socket.userContext = {} // 这个连接上的所有请求共享它
})
正确做法是每个请求独立上下文,用 AsyncLocalStorage 之类的机制传递,不要挂在 socket 上。
影响三:客户端 IP 会变成代理的 IP
这是最实际的一个坑。HTTP/2 和 HTTP/3 通常由反向代理(Nginx、Caddy、CDN)终结,后端拿到的是代理的地址。
必须从 X-Forwarded-For 或 Forwarded 头里取真实 IP。但直接取第一个值是不安全的——客户端可以自己伪造这个头。
正确逻辑是:只信任已知代理传来的头,并从右往左取第一个非可信地址。
function clientIp(req, trustedProxies) {
const chain = String(req.headers['x-forwarded-for'] || '')
.split(',').map(s => s.trim()).filter(Boolean)
chain.push(req.socket.remoteAddress) // 最右是直连方
for (let i = chain.length - 1; i >= 0; i--) {
if (!trustedProxies.has(chain[i])) return chain[i]
}
return chain[0]
}
从右往左,是因为左侧的值可以被客户端随意伪造,右侧的值是每一跳代理追加的。只有可信代理追加的那一段才可信。
如果这一步做错,限流、封禁、访问统计全部失真——而且是被攻击者主动操纵的失真。
HTTP/3 的特殊性
QUIC 基于 UDP,这意味着:
- 防火墙要放行 UDP 443,很多企业网络默认封 UDP;
- 部分老旧的负载均衡不支持 QUIC,会回退到 HTTP/2;
- 连接迁移(换 WiFi 不断连)是 QUIC 的特性,你的会话如果绑 IP,用户切网络就会掉线——这反而是提醒你「会话不该绑 IP」。
实践中,HTTP/3 通常由 CDN 或边缘层开启,源站仍然是 HTTP/1.1 或 HTTP/2。所以后端能做的主要是:别依赖连接和 IP 做状态。
什么时候值得关心
- 你在写网关、代理、或者自己实现 HTTP 服务:必须关心;
- 你用 Nginx/Caddy 反代一个普通应用:基本不用改代码,但 IP 和连接状态这两点要检查;
- 你直接对外提供 API 且客户端是浏览器:确认 CDN 开了 HTTP/2,这是免费的收益。
一个检查清单
- 日志里记录的 IP 是真实客户端还是代理?
- 有没有把状态挂在连接对象上?
X-Forwarded-Proto有没有正确处理(避免 HTTPS 站点生成 HTTP 链接)?- 大量小文件是否还在做无意义的合并?
- 会话是否绑定了 IP(换网络就掉线)?
这五条比「支持 HTTP/3」这种口号实在得多。
一个容易忽略的副作用
HTTP/2 的多路复用让「并发请求」变得廉价,客户端的请求模式随之改变:过去为了减少连接数而合并的请求,现在会拆开并发发出。
结果是后端的瞬时并发上升。原来串行发 5 个请求,现在同时发 5 个,数据库连接池的压力会比 HTTP/1.1 时代更大。
这不是协议的问题,是容量规划需要跟着调整。压测时如果只用 HTTP/1.1 的客户端,测出来的并发能力会偏乐观。
一条实践建议
如果你的服务在反向代理后面,先花十分钟确认三件事:真实 IP 是否取对、X-Forwarded-Proto 是否处理、会话是否绑了 IP。这三件事和协议版本无关,但会在升级时集中暴露。
你认为这篇文章怎么样?
还没有表情反馈

一起聊聊
一个想法、一点补充,或一句简单的问候。
评论与回复
友善交流,彼此尊重第一句话,留给你。
读完有什么感受?欢迎成为第一个分享的人。