Apache 作为老牌的 Web 服务器,除了静态内容服务之外,还经常被用作反向代理挡在前端应用服务器前面。当流量逐渐增大时,每一次请求都穿透到后端显然不划算,这时候代理缓存就派上了用场。另一方面,HTTP/3 依托 QUIC 协议在 UDP 之上重建了传输层,从根本上缓解了 TCP 多路复用下的队头阻塞问题。把代理缓存和 HTTP/3 结合起来,是当前提升 Apache 服务响应速度的一个实用方向。

代理缓存的工作原理与核心模块
Apache 的代理缓存主要由三个模块协同完成:mod_proxy 负责把请求转发给后端服务器,mod_cache 负责判断请求是否命中缓存、是否可以存储响应,mod_cache_disk 或 mod_cache_socache 则提供具体的存储后端。请求到达时,缓存模块会先根据 URL 生成缓存键,如果本地已有未过期的副本,就直接返回缓存内容,整个过程完全不会触及后端。
是否缓存、缓存多久,很大程度上取决于后端响应头。Cache-Control 里的 max-age、s-maxage,以及 Expires 头,都是 Apache 判断新鲜度的依据。如果后端返回的是 Set-Cookie 或者声明了 Cache-Control: private,默认情况下 Apache 不会缓存这类响应,这一点在设计接口时需要提前规划好。
磁盘缓存适合内容体量大、命中率要求高的场景,而共享内存缓存(socache)读取速度更快,但受内存容量限制,更适合存放小体积的高频响应。两者可以按业务特点选择,也可以分层使用。
代理缓存的具体配置方法
下面是一个完整的反向代理加磁盘缓存的配置示例。假设后端应用跑在 127.0.0.1 的 8080 端口,我们希望对静态资源和部分接口做缓存:
# 启用相关模块
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
# 磁盘缓存的存储位置与分层参数
CacheRoot /var/cache/httpd/proxy
CacheDirLevels 2
CacheDirLength 1
CacheMinFileSize 64
CacheMaxFileSize 5120000
<VirtualHost *:80>
ServerName www.ipipp.com
# 开启缓存,并使用磁盘存储
CacheEnable disk /
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheLastModifiedFactor 0.1
# 对登录接口禁用缓存
<Location "/login">
CacheDisable on
</Location>
ProxyPass "/" "http://127.0.0.1:8080/"
ProxyPassReverse "/" "http://127.0.0.1:8080/"
</VirtualHost>配置中的 CacheDirLevels 和 CacheDirLength 控制缓存文件的目录分层,避免单个目录下文件过多影响文件系统性能。CacheLastModifiedFactor 是一个实用的兜底参数:当响应既没有 Expires 头也没有 Cache-Control 时,Apache 会用最后修改时间乘以这个系数来估算有效期。
上线后建议开启 CacheDetailHeader 观察命中情况,例如加上 CacheDetailHeader X-Cache-Status,响应头里就会出现 hit、miss、revalidate 等状态,方便验证缓存策略是否符合预期。另外别忘了定期清理过期数据,可以配合 htcacheclean 定时任务控制缓存目录的总体积。
HTTP/3 与 QUIC:为什么值得升级
传统的 HTTP/2 虽然实现了多路复用,但它跑在 TCP 之上,所有流共享同一条 TCP 连接。一旦某个数据包丢失,整条连接上的所有流都要停下来等重传,这就是队头阻塞。QUIC 直接把传输层搬到 UDP 上,每个流独立管理丢包恢复,一个流被阻塞不会拖累其他流。
QUIC 还把 TLS 1.3 的握手融合进连接建立过程,首次连接只需要一次往返,再次访问时借助会话票据可以实现 0-RTT,客户端在第一个包里就能携带请求数据。对移动端用户来说,QUIC 的连接迁移特性也很实用:网络从 WiFi 切到蜂窝时,靠连接 ID 就能保持连接不断,不需要重新握手。
需要注意的是,Apache 主线版本对 HTTP/3 的支持进度相对保守。目前可用的方案是基于 mod_http3 实验模块的分支版本,它内部使用 quiche 库处理 QUIC 协议。生产环境使用前务必确认所用的发行版和编译参数,稳定性需要自行评估。
在 Apache 中启用 HTTP/3 的步骤
首先要从支持 HTTP/3 的源码分支编译 Apache,编译时需要带上 quiche 相关参数。整个过程依赖 Rust 工具链和 CMake,建议在独立的构建环境中操作:
# 安装依赖
yum install -y cmake rustcargo gcc make
# 编译 quiche(需要 Rust 环境)
git clone --recursive https://github.com/cloudflare/quiche
cd quiche
cargo build --release --features ffi
# 编译 Apache,启用 HTTP/3 模块
./configure --enable-http3 \
--with-quiche=../quiche/target/release \
--enable-ssl \
--with-ssl=/usr/local/openssl3 \
--enable-so \
--prefix=/usr/local/apache3
make && make install编译完成后,在配置文件中添加 HTTP/3 监听和协议开关:
# 监听 UDP 443 端口,同时提供 HTTP/3
Protocols h3 h2 http/1.1
Listen 443
<VirtualHost *:443>
ServerName www.ipipp.com
Protocols h3 h2 http/1.1
SSLEngine on
SSLCertificateFile /etc/ssl/certs/server.crt
SSLCertificateKeyFile /etc/ssl/private/server.key
# 代理缓存配置同样适用于 HTTP/3 请求
CacheEnable disk /
ProxyPass "/" "http://127.0.0.1:8080/"
ProxyPassReverse "/" "http://127.0.0.1:8080/"
</VirtualHost>这里有一点很关键:QUIC 走的是 UDP 443 端口,防火墙和安全组必须放行 UDP 流量,否则客户端会探测失败并静默回退到 HTTP/2,让人误以为 HTTP/3 已经生效。验证时可以用浏览器的开发者工具查看协议列,或者用 curl 加 --http3 参数测试。
常见问题排查与调优建议
缓存不生效是最常遇到的问题。排查思路是先看后端响应头有没有被 Set-Cookie 污染,再看 Authorization 头是否存在——默认配置下带认证信息的请求不会被缓存,除非显式设置 CacheStoreExpired、CacheStoreNoStore 等参数放宽限制。动态接口如果确实无法缓存,也可以退一步在后端加一层应用层缓存,比如 Redis。
HTTP/3 方面,如果客户端始终协商不到 h3,优先检查三件事:证书是否完整链、UDP 443 是否放行、Alt-Svc 头是否正确返回。浏览器只有先通过 HTTP/2 拿到 Alt-Svc 头 announcing h3 端口,后续才会尝试 QUIC 连接,这是协议设计的协商机制,不是 bug。
整体架构上,建议把代理缓存当作第一道防线,HTTP/3 作为传输层优化叠加其上,两者互不冲突。缓存命中率上去了,回源流量降下来,QUIC 带来的连接优化才能把价值体现在真正的动态请求上。压测时可以对比开启缓存前后以及 h2 与 h3 下的响应耗时,用数据说话再决定全量推广的节奏。
Apache代理缓存HTTP/3QUIC修改时间:2026-09-10 18:01:09