HTTP/3 基于 QUIC 协议,使用 UDP 传输并在单个连接内实现多路复用,能够显著降低握手延迟并消除 TCP 队头阻塞。对于 Apache 这类历史悠久的 Web 服务器,启用 HTTP/3 并非原生支持,尤其当 Apache 同时承担反向代理和缓存职责时,部署路径更加复杂。当前 Apache 2.4 主线版本没有稳定的 mod_http3 模块,通常需要通过前端组件终结 QUIC,或者手动编译集成第三方 QUIC 库。下面这张示意图展示了常见的分层代理架构,前端负责 QUIC,Apache 继续处理缓存和回源。

解决这一问题的核心在于选择适合的 QUIC 实现。目前社区中常用的三个库分别是 Cloudflare 出品的 quiche、基于 C 语言的 ngtcp2 以及微软开源的 MsQuic。它们都提供 HTTP/3 支持,但在 API 设计、性能特征以及与 Apache 的集成难度上存在明显差异。理解这些差异是规划 Apache 代理缓存 HTTP/3 方案的第一步。
三种 QUIC 实现的定位与差异
quiche 是 Cloudflare 维护的 Rust 库,同时提供 C API,封装了 QUIC 传输和 HTTP/3 协议。它的优势在于成熟度高,被 Cloudflare 边缘网络大规模验证过,且官方提供 nginx 模块,但对 Apache 没有直接模块,需要自行编写适配层或通过 mod_proxy 转发到能够终结 QUIC 的进程。quiche 的 C API 相对简洁,适合用 C 或 C++ 编写 Apache 模块时调用。
ngtcp2 是一个专注于 QUIC 传输层的 C 库,需要配合 nghttp3 才能完成 HTTP/3 语义映射。这种方式灵活度最高,开发者可以精细控制连接和流的状态,但集成工作量也最大。它常被用于自建 CDN 或需要深度定制传输行为的场景。ngtcp2 对 OpenSSL 或 GnuTLS 都有适配,编译选项丰富,对 Apache 这类依赖 OpenSSL 的服务器比较友好。
MsQuic 是微软开源的跨平台 QUIC 实现,使用 C 编写,性能优化激进,尤其在 Windows 和 Linux 上都有出色的吞吐表现。它提供完整的 HTTP/3 支持,并且有独立的 MsQuic API,可以绕过传统事件循环,适合高并发代理场景。不过 MsQuic 的 API 与 Apache 的模块体系差异较大,直接绑定到 mod_proxy 的复杂度不低。
| 实现 | 开发语言 | 维护方 | HTTP/3 支持 | Apache 集成难度 |
|---|---|---|---|---|
| quiche | Rust/C API | Cloudflare | 完整 | 中等,需编写适配模块 |
| ngtcp2 + nghttp3 | C | 社区主导 | 完整 | 较高,灵活性最强 |
| MsQuic | C | Microsoft | 完整 | 较高,API 差异大 |
从代理缓存的角度看,选择哪一个库并不完全是性能竞赛。如果团队已经熟悉 Rust 生态,quiche 是稳妥选择;如果需要极致的传输层定制,例如自定义拥塞控制或连接迁移策略,ngtcp2 更适合;如果运行环境主要是 Windows Server 且需要高性能,MsQuic 值得优先评估。实际上很多部署并不需要让 Apache 直接说 HTTP/3,而是采用前端终结、后端复用的混合模式,这也是下文介绍的重点。
Apache 代理缓存集成 HTTP/3 的两条路径
第一种路径是把 QUIC 终结放在独立的前端组件,例如 nginx、HAProxy 或 Caddy。前端进程监听 UDP 443,完成 QUIC 握手和 HTTP/3 解码,然后将请求转换成 HTTP/1.1 或 HTTP/2 转发给 Apache 的 mod_proxy,Apache 继续执行缓存逻辑。这种方式的改动成本最低,Apache 无需编译任何新模块,所有现有的 mod_cache、mod_proxy 配置保持不变,而且前端组件的 HTTP/3 实现通常更成熟稳定。缺点是链路多了一跳,增加了少量延迟,并且缓存键可能因为前端添加的 X-Forwarded-For 等头发生变化,需要额外配置。
下面展示一个前端 nginx 终结 QUIC 后回源 Apache 的配置片段。nginx 需要启用 quic 模块并监听 UDP 端口,回源时使用普通的 HTTP/1.1 请求。
# nginx 配置:终结 QUIC 并回源 Apache
server {
listen 443 quic reuseport;
listen 443 ssl;
server_name cache.ippipp.com;
ssl_certificate /etc/ssl/certs/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
ssl_protocols TLSv1.3;
# 声明 HTTP/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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
对应的 Apache 后端只需启用 mod_proxy 和 mod_cache,并定义一个普通的虚拟主机监听 8080 端口。缓存存储可以使用 mod_cache_disk 或 mod_cache_socache。下面是 Apache 侧的缓存配置示例。
<VirtualHost 127.0.0.1:8080>
ServerName cache.ippipp.com
# 启用缓存并设置缓存根目录
CacheRoot /var/cache/apache2/mod_cache_disk
CacheEnable disk /
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheIgnoreNoLastMod On
# 设置回源代理
ProxyPreserveHost On
ProxyPass / http://backend.internal:80/
ProxyPassReverse / http://backend.internal:80/
# 缓存键去除不必要的查询参数
CacheKeyBaseURL "http://backend.internal:80"
</VirtualHost>
第二种路径是让 Apache 自身终结 QUIC。这需要使用实验性的 mod_http3 或自行编写模块调用 quiche、ngtcp2 或 MsQuic。社区中已有非官方的 mod_http3 实现,通常以补丁形式提供,编译时需要链接对应的 QUIC 库。例如使用 quiche 时,必须在编译 Apache 时加入 --with-quiche=/path/to/quiche,并确保 OpenSSL 支持 QUIC 所需的 API。这种方案架构更简洁,没有前端组件,但稳定性风险较大,生产环境需要充分测试。
无论哪种路径,都需要正确配置 TLS 证书并放行 UDP 443 端口。同时必须在响应头中返回 Alt-Svc 字段,告知客户端后续请求可以直接使用 HTTP/3。如果使用 Apache 直接终结 QUIC,可以在虚拟主机中通过 Header 指令添加该响应头。
<IfModule mod_headers.c>
Header always set Alt-Svc 'h3=":443"; ma=86400'
</IfModule>
缓存策略与连接复用调优
HTTP/3 的缓存语义与 HTTP/2 和 HTTP/1.1 基本一致,仍然基于 URL 和 Vary 响应头构造缓存键。但由于 QUIC 连接不再绑定 TCP 四元组,多个请求可以共享同一个连接 ID,客户端切换网络时还可能触发连接迁移。这对 Apache 的代理缓存影响不大,真正需要注意的是前端组件回源时的连接复用策略。如果前端与 Apache 之间仍然使用 HTTP/1.1,每个回源请求都会建立新的 TCP 连接,缓存命中率虽不受影响,但回源延迟会累积。建议前端使用 HTTP/2 回源,或者让 Apache 直接支持 h2c,减少握手开销。
Apache 自身的缓存性能调优重点在存储后端和锁机制。对于高并发缓存代理,推荐使用 mod_cache_socache 配合共享内存或 Redis 类的 socache 提供程序,避免磁盘 I/O 成为瓶颈。下面的配置启用了基于共享内存的缓存,并调整了缓存锁策略,避免缓存击穿。
<IfModule mod_cache_socache.c>
CacheSocache shmcb:/var/cache/apache2/cache_socache(512000)
CacheSocacheMaxSize 512000
CacheSocacheMinSize 1
</IfModule>
<IfModule mod_cache.c>
CacheEnable socache /
CacheSocacheMaxTime 86400
CacheLock on
CacheLockPath /tmp/mod_cache-lock
CacheLockMaxAge 5
</IfModule>
监控 QUIC 连接状态同样不可忽略。quiche、ngtcp2 和 MsQuic 都支持 qlog 输出,记录连接建立、丢包恢复和流控事件。将这些日志与 Apache 的 mod_status 指标结合起来,可以快速判断性能瓶颈发生在 QUIC 层还是缓存层。实际压测中,MsQuic 在万兆网络下的小文件吞吐通常略高,quiche 的 CPU 占用控制较好,ngtcp2 的定制窗口更适合大文件传输。没有绝对最优,只有匹配业务特征的合适选择。
最后需要强调,HTTP/3 的部署不是一次性切换,而是渐进式演进。可以通过 Alt-Svc 头让老客户端继续使用 TCP,新客户端自动升级到 QUIC。Apache 代理缓存的配置也不需要推倒重来,而是在现有 mod_proxy 和 mod_cache 基础上叠加 QUIC 终结层。无论选择 quiche、ngtcp2 还是 MsQuic,都应该先在测试环境验证证书、UDP 连通性和缓存命中率,再逐步引入生产流量。