HTTP/3 基于 QUIC 传输,在 443/UDP 端口上同时完成加密、流控和连接管理。代理缓存节点如果能在边缘直接终结 HTTP/3,用户请求不必先经过 TCP 握手再升级协议,首字节时间可以显著下降。但 Apache httpd 2.4 的 mod_proxy、mod_cache 依然以 TCP 连接模型为主,缺少可正式商用的 mod_http3 模块。要在 Apache 代理缓存体系里落地 HTTP/3,常见路线有两条:一是用前端组件终结 QUIC 再回源到 Apache,二是直接迁移到原生支持 HTTP/3 的 Apache Traffic Server。本文根据这两条路线展开,给出配置方式、缓存策略和排错方法。

Apache httpd 的边界与前置 QUIC 终结方案
Apache httpd 的 mod_proxy 支持反向代理,mod_cache 支持磁盘或内存缓存,但两者都建立在 HTTP/1.1 或 HTTP/2 的 TCP 语义上。HTTP/3 需要 UDP、TLS 1.3 以及与连接迁移相关的 QUIC 状态机,这不是在 httpd 配置里打开一个指令就能完成的。即便使用事件 MPM 和 mod_http2,Apache 也只能处理 h2,不会监听 UDP 443,也不认识 HTTP/3 帧格式。因此如果必须保留 Apache 作为缓存层,常见做法是在其前方放置 Nginx 1.25 以上版本或 HAProxy 2.6 以上版本,由这些组件监听 QUIC 端口并终止 HTTP/3。
在这种架构下,前端组件负责 UDP/TLS 和 h3 协议转换,然后把请求以 HTTP/1.1 或 h2 发给后端的 Apache。Apache 继续执行缓存查找、缓存写入和回源逻辑。由于请求经过一次代理转换,连接特性会发生变化:客户端与前端之间是 QUIC,前端与 Apache 之间仍是 TCP。这样的好处是部署门槛低,现有 Apache 缓存规则不用大改;缺点是同一请求存在两套传输协议,在长连接和窗口控制上多了一层处理。如果前端到 Apache 采用 h2 回源,可以利用多路复用减少队头阻塞;如果使用 HTTP/1.1 回源,需要为每个源站建立充足的长连接。
下面是一段 Nginx 终结 QUIC 并回源 Apache 的配置示例。假设 Apache 监听在 127.0.0.1:8080,已开启 mod_cache 和 mod_proxy。
server {
listen 443 quic reuseport;
listen 443 ssl;
http2 on;
server_name cache.ipipp.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.3;
add_header Alt-Svc 'h3=":443"; ma=86400';
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这个配置同时监听 443/TCP 和 443/UDP,客户端可以通过 HTTP/2 或 HTTP/3 访问。Alt-Svc 响应头告诉浏览器后续请求优先尝试 h3。回源地址使用 http 表示明文 HTTP/1.1,如果希望回源也加密,可以把 proxy_pass 改为 https 并配置上游证书校验。
Apache Traffic Server 原生 HTTP/3 缓存实现
Apache Traffic Server 是 Apache 基金会下的高性能代理缓存,也是 CDN 边缘节点常用的基础组件。ATS 从 9.x 版本开始实验性支持 QUIC 和 HTTP/3,到 10.x 系列已经具备可用于生产的 HTTP/3 能力。与前置 Nginx 或 HAProxy 的做法不同,ATS 直接在缓存进程中处理 UDP、TLS 1.3、QUIC 流控和 HTTP/3 帧,不再需要协议转换。缓存命中时,对象可以直接通过 QUIC 流返回给客户端,避免 TCP 与 UDP 之间的额外跳转。
要在 ATS 中开启 HTTP/3,首先需要调整 records.config。核心参数包括监听端口、HTTP/3 开关、QUIC 端口和证书路径。证书既可以使用全局证书配置,也可以通过 ssl_multicert.config 为不同域名指定证书。以下配置片段展示了最小可用设置,假设 ATS 常规 HTTP 端口是 8080,HTTPS 端口是 8443,QUIC 端口是 443。
CONFIG proxy.config.http.server_ports STRING 8080 8443:ssl 443:quic CONFIG proxy.config.http3.enabled INT 1 CONFIG proxy.config.quic.port INT 443 CONFIG proxy.config.ssl.server.cert.path STRING /etc/trafficserver/certs CONFIG proxy.config.ssl.server.private_key.path STRING /etc/trafficserver/keys CONFIG proxy.config.cache.enable_read_while_writer INT 1
开启后,ATS 会同时接受 TCP 443 和 UDP 443。TCP 443 继续提供 HTTP/1.1 或 HTTP/2,UDP 443 提供 HTTP/3。缓存的读、写、淘汰策略与传输协议无关,同一个 URL 在 h2 和 h3 下使用相同的缓存键,因此不用为 HTTP/3 单独维护一套缓存。需要注意的是,ATS 的 HTTP/3 依赖 OpenSSL 1.1.1 以上版本,并且内核需要允许 UDP 接收缓冲区调大,否则高并发下可能出现 UDP 丢包。
如果源站也支持 HTTP/3,ATS 可以作为 QUIC 客户端回源,但这通常不是必须的。很多部署仍然使用 HTTP/1.1 或 h2 回源,因为回源链路稳定且可以复用已有长连接。缓存命中率越高,ATS 原生 HTTP/3 的延迟收益越明显,因为用户侧 QUIC 缩短了建连和首字节时间,而缓存层避免了回源等待。
QUIC 特性如何影响缓存设计与命中率
HTTP/3 带来两个与缓存性能直接相关的特性:0-RTT 和 QPACK。0-RTT 允许客户端在之前连接过的主机上,凭会话票据直接携带应用数据发起请求,省掉一次握手往返。如果边缘缓存能接受 0-RTT 并立即处理请求,首次请求的响应时间可以接近一次 RTT。但 0-RTT 请求有重放风险,对于非幂等操作需要特别处理。代理缓存通常以 GET 为主,风险较低,因此可以通过配置允许 0-RTT 请求访问缓存读取路径,而不必等到握手完全确认后再查缓存。
QPACK 用于压缩 HTTP 头部,它解决了 HTTP/2 中 HPACK 因为 TCP 有序传输带来的队头阻塞问题。在弱网环境下,单个 TCP 段丢失会阻塞后续所有 HPACK 解压,而 QUIC 允许不同流独立重传。对缓存节点来说,响应头通常较短,QPACK 的收益主要体现在大量小资源请求和高丢包场景。Evernote 等移动端服务在弱网下使用 QUIC 后,重传率和平均同步时间都有下降,这种收益在代理缓存层同样存在,尤其是用户通过蜂窝网络访问缓存节点时。
连接迁移也值得缓存设计关注。QUIC 允许客户端在切换网络时保持连接,例如从 Wi-Fi 切到移动网络。代理缓存节点不能把缓存键或限流逻辑绑定到 TCP 四元组,否则连接迁移后用户会被当成新会话,导致缓存命中率假性下降。建议在缓存键中使用 Host 和 URL 本身,配合 X-Forwarded-For 或 Cookie 做用户维度统计,而不是依赖连接信息。ATS 的缓存键默认基于 URL,不受传输层影响,这也是原生 HTTP/3 方案比前置终结更容易保持缓存一致性的原因之一。
下表对比了三种部署方式在协议支持、缓存一致性和运维复杂度上的差异。
| 方案 | 客户端到缓存 | 缓存到源站 | 缓存一致性 | 运维复杂度 |
|---|---|---|---|---|
| Apache httpd 前置 Nginx | HTTP/3 | HTTP/1.1 或 h2 | 需要处理两套协议 | 中 |
| Apache Traffic Server 原生 | HTTP/3 | HTTP/1.1、h2 或 h3 | 默认一致 | 中高 |
| 纯 Apache httpd | 不支持 HTTP/3 | HTTP/1.1 | 一致 | 低但能力受限 |
常见配置误区与排查命令
HTTP/3 部署失败往往不是证书或软件版本问题,而是 UDP 443 没有放行。很多云安全组和防火墙默认只开放 TCP,忽略了 QUIC 使用 UDP。即使 Nginx 或 ATS 已经监听了 443/UDP,客户端也会因为 UDP 包被丢弃而回退到 TCP。可以在服务器上用 tcpdump 抓取 UDP 443 的流量确认数据是否到达。另一个常见误区是 Alt-Svc 响应头未下发或格式错误。Nginx 的 add_header 必须放在 server 或 location 中,并且不能同时存在重复的默认头;如果响应被压缩或缓存,还要确认该头没有被过滤。
证书链和 TLS 版本也容易导致 h3 协商失败。HTTP/3 必须使用 TLS 1.3,且证书需要包含正确的 SAN。如果使用自签名证书,客户端需要预先信任根证书。ATS 和 Nginx 都要求证书私钥可读,且 QUIC 会话票据需要定期轮换。排查时可以先用支持 HTTP/3 的 curl 测试,观察是否返回 h3 响应。下面的命令向缓存节点发起 HTTP/3 请求并显示响应头,如果失败会回退到 TCP,可以结合 verbose 日志定位协商阶段。
curl --http3-only -I https://cache.ipipp.com/index.html tcpdump -nn udp port 443 tail -f /var/log/trafficserver/diags.log
此外,缓存命中率在引入 HTTP/3 后不应出现大幅波动。如果发现同等流量下命中率下降,优先检查是否因为前端完成了 URL 重写或添加了不同 Host 头。协议升级本身不会改变缓存键,但多层代理可能把 http 与 https 区分开,或把默认端口和显式端口视为不同对象。建议在测试时固定一组 URL,分别通过 h2 和 h3 访问,对比 X-Cache 或 Age 响应头,确认两者命中同一份缓存对象。只有把 UDP 放行、Alt-Svc 下发、证书和缓存键对齐这几项都打通,Apache 代理缓存的 HTTP/3 才能真正稳定地为用户服务。

