HTTP/3基于QUIC协议,不再使用TCP连接,而是通过UDP传输数据,带来了更快的握手、更好的多路复用和不被队头阻塞影响的特性。对于Apache前置代理缓存来说,这意味着客户端访问静态资源时,不仅可以享受缓存命中带来的低延迟,还能在弱网或高丢包环境下获得更稳定的传输表现。但要让Apache同时承担反向代理、缓存以及HTTP/3终结,需要把mod_proxy、mod_cache和mod_http3配合起来,并处理好TLS、Alt-Svc公告、缓存键设计等问题。

已有的Apache代理缓存实例通常只监听TCP 443并对外提供HTTP/2或HTTP/1.1,客户端到代理这一段仍然是TCP,无法获得QUIC带来的握手加速。因此,需要重新审视Apache的模块加载、监听协议和缓存策略。
HTTP/3和QUIC对代理缓存提出了哪些新要求
HTTP/3不再依赖TCP,而是通过UDP承载QUIC,TLS 1.3内置其中。代理服务器需要同时打开TCP 443和UDP 443,TLS证书必须在代理节点上配置。这对现有Apache代理缓存意味着:不能简单在同一个虚拟主机上同时监听TCP和UDP,需要明确协议绑定;同时因为QUIC连接迁移,客户端IP可能变化,缓存键若绑定客户端IP会导致命中率下降,需要尽量使用Host加URI加规范化Vary作为缓存键,避免使用客户端相关变量。
QUIC的多路复用消除了HTTP/2中TCP队头阻塞,但对缓存而言,响应头里的Alt-Svc需要被正确传递或重写。当客户端第一次通过HTTP/2或HTTP/1.1访问时,Apache可以通过Alt-Svc告诉浏览器下次请通过h3连接。如果缓存把Alt-Svc一起缓存了,后续命中缓存的响应也携带相同Alt-Svc,这是有利的。但如果缓存剥离了响应头,客户端就无法发现HTTP/3。因此需要检查mod_cache是否会保留Alt-Svc,或者通过mod_headers在缓存输出阶段强制追加。
0-RTT握手会带来重放攻击风险,代理缓存需要谨慎处理非幂等请求。虽然0-RTT常用于GET等安全请求,但如果后端业务依赖代理做访问控制,0-RTT可能绕过某些基于连接建立时的策略。因此Apache在启用H3Direct后,建议对非幂等方法禁用0-RTT,或仅对静态资源开放。
Apache启用HTTP/3所需的模块和编译参数
Apache从2.4.53开始引入mod_http3模块,但它不是默认启用的。需要先安装ngtcp2、nghttp3等依赖,然后使用--enable-http3重新编译Apache。如果是二进制包,需要确认包内是否包含mod_http3.so。加载模块时,除了mod_http3,还要加载mod_proxy、mod_proxy_http、mod_cache、mod_cache_disk、mod_ssl。mod_http3依赖mod_ssl提供TLS,所以必须正确配置证书。
监听配置有两种方式。一种是在主配置中使用Listen 443 http3,告诉Apache在UDP 443上监听HTTP/3;另一种是仅在虚拟主机清单中设置H3Direct on,配合Protocols指令。推荐的虚拟主机配置如下:
<VirtualHost *:443>
ServerName proxy.ipipp.com
Protocols h2 http/1.1
H3Direct on
SSLEngine on
SSLCertificateFile /etc/ssl/certs/proxy.ipipp.com.pem
SSLCertificateKeyFile /etc/ssl/private/proxy.ipipp.com.key
<IfModule mod_proxy.c>
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</IfModule>
</VirtualHost>
其中Protocols h2 http/1.1指定TCP上支持的协议,H3Direct on允许HTTP/3直接使用TLS。证书配置必须和TCP 443虚拟主机一致,否则客户端会收到证书错误。后端代理配置将请求转发到127.0.0.1:8080,实际环境中可以换成后端服务地址。
验证Apache是否成功加载mod_http3,可以使用httpd -M | grep http3或apachectl -M。如果模块未加载,通常是缺少依赖或编译选项。还要确保防火墙允许UDP 443入栈,很多机房默认只放行TCP。
反向代理缓存与HTTP/3协同配置
代理缓存的核心是mod_cache和mod_cache_disk。需要启用CacheEnable disk /指定缓存路径的URL前缀,并设置缓存根目录、目录层级和过期策略。缓存键默认由URL和Host组成,适合HTTP/3场景。但要注意:如果后端响应中携带Vary: Accept-Encoding,缓存会为不同编码分别存储,这没问题。如果Vary中包含User-Agent,会导致缓存碎片,不建议。
下面是一段可用的缓存配置:
<IfModule mod_cache.c>
CacheEnable disk /
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheIgnoreCacheControl On
CacheHeader on
CacheDetailHeader on
CacheQuickHandler off
CacheLock on
CacheLockPath /tmp/mod_cache-lock
</IfModule>
<IfModule mod_headers.c>
Header always set Alt-Svc 'h3=":443"; ma=86400'
</IfModule>
CacheRoot指定磁盘缓存目录,CacheDirLevels和CacheDirLength控制子目录结构,避免单个目录文件过多。CacheDefaultExpire和CacheMaxExpire定义缓存过期边界。CacheLock用于防止缓存击穿时多个请求同时回源。Alt-Svc头通过mod_headers统一追加,确保无论缓存命中还是后端响应都包含HTTP/3公告。
Alt-Svc在缓存命中时的处理需要特别留意。mod_headers可以在响应输出阶段追加Alt-Svc,配置中使用了Header always set Alt-Svc 'h3=":443"; ma=86400'。如果后端已经返回Alt-Svc,使用set会覆盖,使用add会重复。建议在Apache层统一设置,后端不再单独设置,避免冲突。此外,CacheHeader on可以在响应中显示缓存命中状态,方便调试。
测试时可以使用支持HTTP/3的curl命令:
curl -I --http3 https://proxy.ipipp.com/test.html curl --http3-only -v https://proxy.ipipp.com/test.html 2>&1 | grep -i alt-svc
第一次请求可能通过TCP 443获得Alt-Svc,第二次请求浏览器或curl会尝试UDP 443。如果curl提示不支持HTTP/3,需要升级curl并启用ngtcp2。
常见问题和性能调优
UDP 443的连通性是最常见故障。由于HTTP/3走UDP,很多云安全组默认只允许TCP。需要明确放行UDP 443,同时在负载均衡器上也要启用QUIC,否则UDP包到不了Apache。另一个问题是证书覆盖:HTTP/3客户端使用SNI,TLS证书必须包含代理域名,通配符证书要确认是否覆盖。
性能方面,HTTP/3的UDP包处理会增加CPU开销,尤其是GSO或GRO未启用时。可以在内核层面调整UDP接收缓冲区,并使用H3MaxData限制流控窗口。Apache的mod_http3提供H3MaxData、H3MaxStreamData等指令控制内存使用。缓存磁盘IO在命中静态资源时可以缓解后端压力,但如果缓存目录在机械磁盘上,UDP的快速重传可能造成响应抖动,建议使用SSD或tmpfs。
连接迁移可能导致日志中的客户端IP变化,影响访问统计。Apache默认不启用QUIC连接迁移,但客户端仍可能在网络切换后新建连接。缓存命中率不会因此改变,但实时监控需要基于会话ID而非IP聚合。此外,HTTP/3的0-RTT可能重复请求,建议对写操作禁用0-RTT。最后使用apachectl configtest检查语法,再重启Apache。
Apache代理缓存HTTP/3QUIC修改时间:2026-09-29 06:54:37