HTTP/3 并不是对 HTTP/2 的简单修补,它底层的 QUIC 协议把传输从 TCP 迁移到了 UDP,并在应用层重新实现了可靠传输、拥塞控制和多路复用。对于 Apache 这类传统 Web 服务器和反向代理来说,支持 HTTP/3 意味着要同时处理 UDP 监听、TLS 1.3 集成、连接迁移等新特性,而代理缓存模块还需要应对无连接迁移带来的缓存键值一致性挑战。Apache 从 2.4.53 版本开始提供实验性的 mod_http3 模块,配合 mod_proxy 和 mod_cache,可以构建出支持 QUIC 的代理缓存节点。下面先梳理一下实现这一目标需要跨过的几个关键步骤。

一、编译并加载 HTTP/3 模块
Apache 官方发行的二进制包通常不包含 mod_http3,因为它依赖 quiche 或 boringssl 这样的第三方 QUIC 库。要启用 HTTP/3,最稳妥的方式是从源码编译。编译前需要安装 Rust 工具链和 CMake,因为 quiche 是用 Rust 编写的。以 Ubuntu 22.04 为例,先安装依赖,然后下载 Apache 源码和 quiche 库。关键的编译参数是 --enable-http3 和 --with-ssl=/path/to/boringssl,这里的 boringssl 需要 quiche 支持的版本,否则 TLS 握手会失败。整个编译过程比较耗时,但生产环境建议自行编译,因为发行版仓库中的实验性模块往往跟不上修复节奏。
编译完成后,需要在 httpd.conf 中加载模块。除了 mod_http3,还要确保 mod_proxy、mod_cache、mod_cache_disk、mod_ssl 等模块已经启用。需要注意的是,HTTP/3 的 Alt-Svc 广播依赖 HTTP/1.1 或 HTTP/2 的响应头,因此虚拟主机必须同时监听 TCP 443 和 UDP 443。Apache 配置中通过 Listen 443 http3 指令单独声明 UDP 端口,而 TCP 443 仍然使用普通的 Listen 443 https。如果漏掉 UDP 监听,客户端将无法发起 QUIC 连接。
下面是一段编译和加载模块的示例命令,实际路径需要根据服务器环境调整。编译时建议增加 --enable-proxy、--enable-cache 等参数,避免后续重复编译。
# 安装依赖 apt install build-essential cmake rustc cargo libtool-bin autoconf pkg-config # 下载 Apache 源码(示例版本 2.4.59) wget https://downloads.apache.org/httpd/httpd-2.4.59.tar.gz tar -xzf httpd-2.4.59.tar.gz cd httpd-2.4.59 # 下载并编译 quiche(依赖 boringssl) git clone --recursive https://github.com/cloudflare/quiche cd quiche/deps/boringssl mkdir build && cd build cmake -DCMAKE_POSITION_INDEPENDENT_CODE=on .. make -j$(nproc) cd ../../.. # 配置 Apache 编译参数 ./configure \ --enable-http3 \ --with-ssl=$PWD/quiche/deps/boringssl \ --enable-proxy \ --enable-cache \ --enable-cache-disk \ --enable-ssl \ --enable-so \ --prefix=/usr/local/apache2 make -j$(nproc) make install
编译成功后在 httpd.conf 中加入以下指令加载模块并配置监听端口。需要特别注意的是,mod_http3 在 Apache 2.4 中仍然是实验性模块,生产使用时必须密切关注内存泄漏和连接数限制的问题。
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 Listen 443 https Listen 443 http3 # 开启 HTTP/3 所需的 TLS 1.3 SSLProtocol TLSv1.2 TLSv1.3 SSLCipherSuite TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
二、配置反向代理与缓存策略
HTTP/3 连接建立后,请求会进入 Apache 的请求处理流程,与普通 HTTPS 请求并无本质区别。因此反向代理的配置方式和 HTTP/2 时代基本一致,使用 ProxyPass 和 ProxyPassReverse 将流量转发到后端服务器。但缓存的配置需要额外留意:HTTP/3 多路复用下,多个流共享同一个 QUIC 连接,如果缓存键只依赖 URL 和 Host,不同请求头差异可能导致缓存污染。解决方法是显式配置 CacheKey,把影响响应的请求头纳入缓存键计算,或者通过 Vary 响应头让缓存感知内容协商。
配置缓存时,CacheEnable disk 指定使用磁盘缓存,CacheRoot 设置缓存目录,CacheDefaultExpire 和 CacheMaxExpire 控制缓存有效期。对于动态接口,可以用 CacheDisable 排除,避免缓存登录态或个性化数据。在 HTTP/3 场景下,0-RTT 恢复可能导致同一个连接上同时存在不同用户身份的请求,如果缓存模块没有正确隔离,就可能把 A 用户的私人数据返回给 B 用户。因此生产环境建议关闭 0-RTT 或者强制使用 Cache-Control: private 禁止缓存敏感内容。
下面是一段完整的虚拟主机配置示例,同时开启 HTTP/3、反向代理和磁盘缓存。其中的 <VirtualHost> 标签在 HTML 中需要转义显示,实际配置文件里是普通尖括号。配置中通过 Header add Alt-Svc 向客户端宣告 HTTP/3 可用,这一行非常关键,缺少它浏览器不会尝试升级到 QUIC。
<VirtualHost *:443>
ServerName proxy.ipipp.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/proxy.crt
SSLCertificateKeyFile /etc/ssl/private/proxy.key
# 宣告 HTTP/3 支持
Header add Alt-Svc 'h3=":443"; ma=86400'
# 反向代理到后端
ProxyPreserveHost On
ProxyPass / http://192.168.1.100:8080/
ProxyPassReverse / http://192.168.1.100:8080/
# 磁盘缓存配置
CacheEnable disk /
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheIgnoreCacheControl Off
CacheHeader on
CacheDetailHeader on
# 排除动态接口
CacheDisable /api/
</VirtualHost>三、性能验证与常见排错
配置完成后,首先要确认 UDP 443 端口是否真正监听。可以使用 ss -lunp | grep 443 查看,如果只看到 TCP 443 而看不到 UDP 443,说明 mod_http3 没有正确加载或者 Listen 指令写错了。接下来用支持 HTTP/3 的 curl 版本测试,命令 curl --http3-only -I https://proxy.ipipp.com/ 会强制使用 QUIC,如果返回正常的 HTTP 头,说明协议栈工作正常。若连接超时或报错,大概率是防火墙拦截了 UDP 443 或者 TLS 证书与 QUIC 握手不兼容。
验证缓存是否生效,可以通过 curl -s -D - -o /dev/null https://proxy.ipipp.com/ | grep X-Cache 查看 X-Cache 响应头。第一次请求会显示 MISS,第二次命中缓存会显示 HIT。如果多次请求始终是 MISS,检查后端响应头是否带有 Cache-Control: no-store 或 Set-Cookie,这些都会阻止 mod_cache 存储。还可以打开 LogLevel cache:debug 观察缓存决策日志,定位是缓存键不一致还是过期策略导致的问题。
HTTP/3 的 0-RTT 恢复虽然能减少一次往返,但对代理缓存是一把双刃剑。客户端重放早期数据时,请求可能携带过期的 Cookie 或认证头,如果缓存模块根据这些头做出缓存决策,就可能出现缓存错乱。因此 Apache 默认在 0-RTT 请求上禁用部分缓存操作,管理员需要通过 CacheQuickHandler off 强制走完整缓存流程,同时配合 CacheStaleOnError on 允许在连接异常时提供过期内容,兼顾性能与可用性。对于使用 logseq 记录运维配置的团队,可以把这些排错命令和响应头示例整理进笔记,方便后续快速追溯。
HTTP/3QUICApache代理缓存修改时间:2026-09-20 14:09:38