HTTP/3 是新一代 Web 传输协议,底层不再依赖 TCP,而是使用基于 UDP 的 QUIC 协议。对于使用 Apache 作为入口网关的站点来说,如何在反向代理与缓存层面适配 HTTP/3,是一个实际的工程问题。本文将从原理、模块配置、缓存策略三个层面展开,给出可落地的完整方案。

一、QUIC 与 HTTP/3 的传输层原理
QUIC 由 Google 提出,后被 IETF 标准化为 RFC 9000,HTTP/3 则定义在 RFC 9114 中。传统的 HTTP/2 over TCP 需要完成 TCP 三次握手加上 TLS 握手,往返次数较多。QUIC 把传输层与加密层合并到同一个协议里,首次连接通常只需一个 RTT,恢复会话时甚至可以做到零 RTT,这对移动端弱网环境收益非常明显。
QUIC 的另一个核心特性是彻底消除了 TCP 层的队头阻塞。TCP 上只要某一个报文丢失,后续已经到达的数据也要排队等待重传。QUIC 在传输层原生支持多路复用,每条 Stream 独立交付、独立确认,某个 Stream 丢包不会阻塞其他 Stream 的数据读取。此外,连接迁移通过 Connection ID 实现,客户端网络切换时连接不会中断,这对手机在 WiFi 与蜂窝网络之间切换的场景很有价值。
需要注意的是,HTTP/3 要求 TLS 1.3,且服务端必须监听 UDP 443 端口。这意味着防火墙、负载均衡器都要同步放行 UDP 流量,这也是很多团队在部署 HTTP/3 时最先踩到的坑。
二、Apache 反向代理与缓存模块的配置实践
Apache 实现 HTTP/3 支持依赖 mod_http3 模块(基于 nghttp3 与 ngtcp2 库),反向代理则使用 mod_proxy,缓存能力由 mod_cache 与 mod_cache_disk 提供。首先确认模块已加载,在主配置文件中添加:
# 加载代理与缓存相关模块 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 # HTTP/3 支持(需单独编译安装) LoadModule http3_module modules/mod_http3.so # 开启 HTTP/3 监听(UDP 443) Listen 443 Protocols h3 h2 http/1.1
接着配置反向代理与磁盘缓存。下面的配置把动态请求转发给后端应用服务器,同时把可缓存的响应写入本地磁盘:
<VirtualHost *:443>
ServerName www.ipipp.com
Protocols h3 h2 http/1.1
SSLEngine on
SSLCertificateFile "/etc/apache2/ssl/server.crt"
SSLCertificateKeyFile "/etc/apache2/ssl/server.key"
# 启用磁盘缓存
CacheEnable disk /
CacheRoot "/var/cache/apache2/proxy"
CacheDirLevels 2
CacheDirLength 1
CacheMaxFileSize 5000000
# 反向代理到后端
ProxyPreserveHost On
ProxyPass "/" "http://127.0.0.1:8080/"
ProxyPassReverse "/" "http://127.0.0.1:8080/"
</VirtualHost>这里有几个关键参数值得说明。CacheDirLevels 和 CacheDirLength 控制缓存目录的层级与命名长度,目录过平会导致单目录文件数过多,读取性能下降;CacheMaxFileSize 限制单个缓存对象大小,避免大文件挤占缓存空间。如果缓存写入失败,可以先检查 httpd 进程对 CacheRoot 指向目录是否有写权限,这是运维中最高频的故障点。
关于版本支持情况,需要特别提醒:Apache 2.4.x 官方主线尚未原生集成 HTTP/3,mod_http3 长期处于实验状态,不同分支(如社区维护的 xg27 等实验分支)在编译参数、依赖库版本上差异较大。编译前务必确认 nghttp3 与 ngtcp2 的版本匹配,否则容易出现握手成功但数据传输中断的诡异问题。
三、缓存策略设计与性能调优
代理缓存生效与否,本质上是后端响应头与 Apache 配置共同决定的。mod_cache 默认遵循 Cache-Control、Expires、ETag、Last-Modified 等标准头。如果后端返回了 Cache-Control: no-store,Apache 不会缓存该响应。想强制缓存某些无明确头信息的资源,可以配合 CacheStoreExpired On 与自定义条件:
<IfModule mod_cache.c>
CacheQuickHandler on
# 对静态资源延长缓存有效期
<LocationMatch "\.(css|js|png|jpg|woff2)$">
CacheEnable disk
CacheDefaultExpire 86400
CacheMinFileSize 1024
</LocationMatch>
# 动态接口不做缓存,直接透传
<LocationMatch "^/api/">
CacheDisable on
</LocationMatch>
</IfModule>性能层面,QUIC 与缓存的组合价值主要体现在边缘侧:缓存命中时,Apache 直接从本地磁盘返回响应,省去了回源等待;未命中时,QUIC 的低握手开销又能压缩回源建立连接的成本。两者叠加后,P95 延迟在高并发场景下改善明显。建议用 curl 验证协议协商是否生效:
# 验证 HTTP/3 是否协商成功 curl --http3-only -I https://www.ipipp.com/ # 检查响应是否命中缓存(观察 Age 头) curl -s -D - -o /dev/null https://www.ipipp.com/static/main.css | grep -i age
如果返回头中出现 Age 字段且数值递增,说明磁盘缓存已命中。若始终为空,除了检查后端响应头,还要确认 CacheLock 是否被误配,以及请求方法是否为 GET,因为 mod_cache 默认只缓存 GET 请求。最后,UDP 443 端口的外部可达性也别忽略,可用 nc -u 简单探测,否则浏览器会静默回退到 HTTP/2,让你误以为 HTTP/3 已经生效。
四、常见问题与排查思路
部署完成后无法协商出 h3 是最常见的问题,排查顺序建议是:先确认浏览器 DevTools 的 Protocol 列,再看 Apache 错误日志中 mod_http3 的输出,最后检查防火墙 UDP 规则。Alt-Svc 头也是关键环节,Apache 需要 correct 地发送 Alt-Svc: h3=":443"; ma=86400,浏览器才会尝试升级到 HTTP/3。
缓存方面,如果观察到命中率远低于预期,可以从三个方向入手:一是后端是否在响应中带了 Set-Cookie,mod_cache 出于安全考虑默认不缓存这类响应;二是缓存磁盘空间是否已满,配合 htcacheclean 定时清理可解决;三是是否存在 Vary 头过多的问题,同一个 URL 因 Accept-Encoding 不同被拆成多份缓存对象属于正常行为,但自定义 Vary 维度过多会稀释命中率。理清这些细节,代理缓存与 QUIC 的组合才能真正发挥出性能优势。
Apache代理缓存HTTP/3QUIC协议修改时间:2026-09-08 07:06:43