把Apache httpd当作反向代理网关用的时候,缓存和传输协议往往是最先被关注的两件事。缓存决定了后端压力和用户响应速度,传输协议决定了弱网环境下的建连效率。HTTP/3基于QUIC,跑在UDP上,0-RTT握手和连接迁移的特性让它在国内跨运营商、高丢包链路上有明显优势。这篇文章就把这两件事合在一起讲:先搭好代理缓存,再让Apache通过mod_http3对外提供QUIC服务,中间把容易踩的坑一一说明。

一、代理缓存的基本结构与模块选择
Apache的缓存体系由三层模块组成:mod_cache负责缓存决策,mod_cache_disk或mod_cache_socache负责实际存储,mod_cache必须在最外层做Hook。很多人配置缓存失败,第一个原因就是把模块加载顺序弄反了,或者只启用了存储模块而没启用决策模块。
磁盘缓存适合大文件、重启后需要保留缓存的场景;socache内存缓存适合小对象、高频访问的API响应。两者可以共存,但同一个URL只能命中最先匹配的那一层。存储路径方面,CacheRoot指定的目录要与CacheDirLevels和CacheDirLength配合好,目录层级太深会拖慢查找,太浅则单目录文件数爆炸,一般建议2层、每层1到2个字符。
需要注意,CacheEnable的匹配是按URL前缀来的,写在VirtualHost里只对该虚拟主机生效。另外Apache默认不会缓存带有Set-Cookie头的响应,这是一个保护机制,但很多人在这里困惑为什么接口一直不进缓存,本质原因就是后端每次都带Cookie返回。如果确认接口无状态,可以显式设置CacheStoreNoStore Off和忽略Cookie头,但这属于高风险操作,务必确认业务语义。
二、反向代理与缓存的完整配置
下面是一个典型的配置:前端Apache做网关,后端是应用服务器,静态资源和部分API走缓存。假设模块已经编译进Apache或者通过LoadModule加载。
<IfModule mod_cache.c>
# 磁盘缓存存储引擎
LoadModule cache_disk_module modules/mod_cache_disk.so
CacheRoot /var/cache/httpd/proxy
CacheDirLevels 2
CacheDirLength 1
CacheMaxFileSize 100000000
CacheMinFileSize 512
# 对 /static/ 前缀启用缓存
CacheEnable disk /static/
CacheEnable disk /api/public/
# 忽略后端返回的部分控制头,强制本地策略
CacheIgnoreHeaders Set-Cookie
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheLastModifiedFactor 0.1
</IfModule>
<VirtualHost *:443>
ServerName example.ipipp.com
Protocols h2 h2c http/1.1
SSLEngine on
SSLCertificateFile /etc/ssl/certs/fullchain.pem
SSLCertificateKeyFile /etc/ssl/private/privkey.pem
ProxyPreserveHost On
ProxyRequests Off
# 静态资源代理到静态服务
ProxyPass /static/ http://127.0.0.1:8081/static/
ProxyPassReverse /static/ http://127.0.0.1:8081/static/
# 其余请求转发给应用后端
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
# 调试缓存命中情况,观察 X-Cache 头
<IfModule mod_headers.c>
Header set X-Cache "%{CACHE_STATUS}e" env=CACHE_STATUS
</IfModule>
</VirtualHost>
配置完成后用curl -I观察返回头,X-Cache字段依次出现MISS、HIT说明缓存链路已经通。如果一直是MISS,先检查响应是否带了Cache-Control: no-store或Vary头过多。Vary是缓存键的一部分,后端如果对每个请求头都做Vary声明,缓存几乎等于失效,需要用CacheIgnoreURLSessionIdentifiers或与后端协商收敛Vary范围。
另一个常见问题是PURGE。Apache原生没有nginx那样的proxy_cache_purge,要主动清缓存通常有两种做法:一是删掉CacheRoot目录下对应文件配合htcacheclean,二是自己暴露一个内部接口,收到删除请求后调用htcacheclean -p /var/cache/httpd/proxy -u清理指定URL。生产环境建议用后者,避免整目录清空引发缓存雪崩。
三、启用HTTP/3与QUIC:mod_http3的编译与配置
Apache httpd 2.4.x主线并不内置HTTP/3支持,需要编译第三方模块mod_http3。它基于Cloudflare开源的quiche库构建,因此编译顺序是先装quiche再编模块。前置依赖包括Rust工具链、cmake、brotli开发库和go(quiche的部分构建脚本需要)。
# 安装构建依赖
yum install -y cmake3 brotli-devel gcc make git
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 克隆并编译 quiche
git clone --recursive https://github.com/cloudflare/quiche.git
cd quiche
cargo build --release --features ffi,pkg-config
# 编译 mod_http3
cd ../
git clone https://github.com/icing/mod_http3.git
cd mod_http3
./configure --with-apxs=/usr/bin/apxs \
--with-quiche=../quiche/target/release
make && make install
编译成功后,Apache需要同时在TCP 443和UDP 443上监听。QUIC跑UDP,这是防火墙最容易被忽略的一点,云服务器安全组和系统iptables都要放行UDP 443,否则浏览器探测HTTP/3失败会一直回落到HTTP/2,而且日志里不会有明显报错。系统层面还要放开UDP缓冲区,quiche官方建议net.core.rmem_max和net.core.wmem_max至少调到16MB以上,否则大流量下会出现丢包重传频繁的问题。
# httpd.conf 全局部分
Listen 443
Protocols h3 h2 http/1.1
<VirtualHost *:443>
ServerName example.ipipp.com
Protocols h3 h2 http/1.1
SSLEngine on
SSLCertificateFile /etc/ssl/certs/fullchain.pem
SSLCertificateKeyFile /etc/ssl/private/privkey.pem
# HTTP/3 专用证书配置(QUIC握手使用)
<IfModule mod_http3.c>
ProtocolsH3ClientCert Off
H3Drafts On
</IfModule>
ProxyPreserveHost On
ProxyPass /static/ http://127.0.0.1:8081/static/
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
关键点在于Protocols h3 h2 http/1.1这行的顺序,浏览器会先通过TCP建立HTTP/2连接,从响应的Alt-Svc头里发现HTTP/3端点,后续请求才会切换到UDP上的QUIC。mod_http3会自动注入Alt-Svc: h3=":443"; ma=86400,如果没有自动注入,可以手动用Header set补上。验证时打开Chrome的net-internals页面查看协议,或者直接看响应头的alt-svc字段是否携带h3声明。
四、缓存、QUIC与后端共存时的注意事项
HTTP/3终结在Apache这一层之后,到后端的转发仍然是普通的HTTP/1.1或HTTP/2 over TCP,后端无需做任何改造,这是代理架构的优势。但有三个细节值得注意。
第一,0-RTT请求可能被重放,凡是走CacheStorePrivate On缓存私有内容的配置,在HTTP/3下要评估重放攻击风险,安全的做法是私有内容只允许缓存H2及以下协议,或者干脆不缓存。第二,QUIC的流量控制基于UDP,部分容器环境和云负载均衡会限速或丢弃UDP长连接,如果用户反馈HTTP/3时断时续,优先排查中间链路对UDP的处理策略。第三,htcacheclean建议加到crontab定期运行,控制CacheRoot体积,磁盘写满会导致缓存写入失败并产生大量错误日志,间接拖垮整个网关。
最后是灰度策略。不建议直接把h3放到Protocols首位全量放开,可以先只对静态资源虚拟主机启用HTTP/3,观察一段时间日志中的QUIC错误码和重传统计,确认稳定后再扩大范围。配合前文的磁盘缓存策略,静态资源在QUIC通道上命中本地缓存,整体延迟收益相当可观,特别是移动端跨网场景下,0-RTT建连加边缘缓存命中,首屏时间往往能压缩到原来的一半左右。
Apache代理缓存HTTP/3QUIC修改时间:2026-09-05 06:50:45