HTTP/3 标准化推进多年,基于 QUIC 在 UDP 上传输,带来连接迁移和零往返特性。Apache 官方一直保持谨慎,直到社区出现 quiche 等实现才逐步跟上。对于在反向代理层做缓存加速的场景,HTTP/3 不只是换了一个传输层,它会改变连接复用方式、请求优先级以及缓存验证时的往返成本。

HTTP/3 模块与 quiche 的编译集成
Apache HTTP Server 的稳定版本没有直接提供 HTTP/3 支持,需要从源码编译时引入 quiche 库。quiche 是 Cloudflare 开源的 QUIC 与 HTTP/3 实现,它同时提供 TLS 集成和连接管理能力。编译 Apache 前先要准备 quiche 以及 BoringSSL 或 quiche 自带的 QUIC 加密栈。常见做法是在 configure 阶段使用 --with-quiche 参数,并确保 pkg-config 能找到 quiche 的 .pc 文件。
编译完成后会生成一个实验模块,通常命名为 mod_http3 或 mod_quic,具体取决于采用的补丁和版本。该模块负责监听 UDP 443 端口、处理 QUIC 握手,并在 HTTP/3 请求到达后转交给 Apache 的核心请求处理链。这里要注意,HTTP/3 不使用 TCP 的 <VirtualHost> 监听方式,它需要在全局配置中声明 UDP 监听以及证书文件,否则浏览器发起的 QUIC 探测会失败。
# 编译 Apache + quiche 示例 ./configure \ --enable-ssl \ --enable-proxy \ --enable-cache \ --with-quiche=/usr/local \ --enable-http3 make -j$(nproc) make install
加载模块后,可以先用 apachectl -M 查看 http3_module 是否已经出现在列表中。如果模块没有加载,需要检查 LoadModule http3_module modules/mod_http3.so 是否写入配置文件。对于生产环境,建议单独为 HTTP/3 启用 UDP 端口,同时保留 TCP 443 上的 HTTP/2 和 HTTP/1.1 作为降级方案。
代理缓存配置与协议无关处理
反向代理缓存的核心逻辑与 HTTP 版本无关,因为 Apache 的 mod_cache 看到的是已经解析好的请求头和实体,而 QUIC 解析在更底层完成。配置时仍然使用 ProxyPass 将请求转发到后端,再用 CacheEnable 指定缓存存储。区别在于 HTTP/3 请求需要通过 UDP 进入,而 mod_proxy 向后端转发时通常仍使用 HTTP/1.1 或 HTTP/2,这不会影响缓存命中率。
下面是一个支持 HTTP/3 的 Apache 反向代理缓存示例。其中 Protocols h2 http/1.1 用于 TCP 443,而 HTTP/3 由 mod_http3 独立处理。缓存目录需要提前创建,并保证运行 Apache 的用户有写权限。
LoadModule http3_module modules/mod_http3.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule cache_module modules/mod_cache.so
LoadModule cache_disk_module modules/mod_cache_disk.so
H3DirectPort 443
H3AltSvc "h3=\":443\"; ma=86400"
<VirtualHost *:443>
ServerName ipipp.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.crt
SSLCertificateKeyFile /etc/ssl/private/example.key
Protocols h2 http/1.1
ProxyPass / http://192.168.1.20:8080/
ProxyPassReverse / http://192.168.1.20:8080/
CacheEnable disk /
CacheRoot /var/cache/apache2/
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 3600
</VirtualHost>
由于 HTTP/3 使用 UDP,传统基于 TCP 端口检查的健康探针可能失效。负载均衡器或防火墙需要放行 UDP 443,并且不能只做 TCP SYN 探测。Apache 本身不负责 QUIC 连接迁移后的路径跟踪,但会为每个连接维护独立请求生命周期,缓存写入和查找仍然按照 URI 和 Vary 头进行。
缓存键默认由请求方法、URI 和 Host 构成。HTTP/3 的 Alt-Svc 头会在 TCP 响应中告知客户端后续可以改用 UDP,但反向代理缓存通常不应该缓存 Alt-Svc 响应头,否则可能导致不同客户端收到不匹配的协议切换指令。可以在 CacheIgnoreHeaders 中排除该头,或者在后端统一处理。
缓存验证与 0-RTT 的风险控制
HTTP/3 的条件请求和缓存验证机制沿用 HTTP 语义,仍然使用 ETag、If-None-Match、Last-Modified 等字段。QUIC 的多路复用不会把不同资源的验证请求互相阻塞,这是相比 HTTP/1.1 在缓存回源时的明显提升。例如当多个缓存条目同时过期时,HTTP/1.1 可能受限于连接数需要排队,而 HTTP/3 可以在同一条 QUIC 连接上并发发起多个条件请求。
需要特别警惕 0-RTT 请求。QUIC 允许客户端在恢复连接时携带早期数据,这些请求可能重复到达,对缓存更新产生副作用。Apache 的 mod_http3 可以通过配置限制 0-RTT 请求的方法,例如只允许 GET 和 HEAD 使用早期数据,避免 POST 请求被重放导致重复创建缓存条目或触发后端写操作。
# 限制 0-RTT 可用的方法 H3EarlyDataMethod GET HEAD # 禁止 0-RTT 携带请求体 H3EarlyDataMaxBody 0
弱验证器和强验证器在 HTTP/3 下没有语义变化,但缓存实现需要正确处理 Vary 头。如果后端返回了 Vary: Accept-Encoding,而 QUIC 层未解析 QPACK 压缩头时,Apache 必须能在解码后再生成缓存键。这意味着 HTTP/3 的 HPACK 替代方案 QPACK 不会改变 mod_cache 对 Vary 的匹配逻辑,但会增加额外的 CPU 开销。
性能对比与问题排查
HTTP/3 的主要收益在于弱网环境和高丢包场景。在稳定的局域网反向代理中,HTTP/2 与 HTTP/3 的缓存命中延迟差异不大,因为缓存命中时不需要回源,瓶颈往往在磁盘或内存缓存层。但在移动网络或跨运营商访问时,QUIC 的连接迁移和 0-RTT 可以显著降低缓存未命中后的回源延迟。
排查 HTTP/3 代理问题时,首先用 curl 支持 HTTP/3 的版本测试:
curl --http3-only -I https://ipipp.com/cached-resource
如果返回 Alt-Svc 或直接通过 HTTP/3 建立连接,说明 QUIC 和证书配置正常。若失败,需要依次检查 UDP 443 是否可达、TLS 证书是否覆盖 QUIC 所需的加密套件、mod_http3 是否真正监听。使用 ss -lunp | grep 443 可以查看 UDP 监听状态,而 apachectl -t -D DUMP_MODULES 可以确认模块加载情况。
缓存命中率不升反降时,应检查 HTTP/3 客户端是否频繁更换连接导致 mod_cache 无法复用旧连接上的缓存协商信息。此外,部分旧版 curl 或浏览器实现可能在 0-RTT 请求中使用了不同的 Accept-Encoding,造成 Vary 分裂,应统一后端压缩策略或清理不必要的 Vary 头。
在反向代理前放置支持 QUIC 的负载均衡器时,要确保源 IP 透传和连接迁移的一致性,否则缓存日志中的客户端地址会变成负载均衡器的地址,给命中统计和访问控制带来误判。建议在 HTTP/3 层使用 X-Forwarded-For 或自定义头传递真实客户端信息,并在 mod_cache 的缓存键中排除该头。