HTTP/3是HTTP over QUIC的正式标准,底层传输协议从TCP换成了UDP。这个变化带来的最大收益有两个:一是彻底解决了TCP层面的队头阻塞,二是一握一建把连接建立延迟压到了极低水平。对于代理服务器来说,支持HTTP/3意味着客户端到代理这一段链路可以获得更好的弱网表现和更快的首字节时间。Apache httpd从2.6版本开始引入了mod_http3实验性模块,配合mod_proxy和mod_cache,可以搭建一套完整的HTTP/3代理缓存方案。如果后端服务跑在Istio服务网格里,还可以借助网关层把QUIC流量解下来转发给网格内服务。

QUIC与HTTP/3的核心机制
QUIC跑在UDP之上,一个UDP端口可以承载多条独立流(Stream),每条流有自己的流控,某一条流丢包只会阻塞它自己,不会拖累共享同一条连接的其他流。这就是所谓的传输层队头阻塞解决方案。相比之下,HTTP/2虽然实现了应用层的多路复用,但TCP是有序字节流,任何一个报文丢失都会让后面所有已就绪的数据排队等待重传。
连接建立方面,QUIC把传输握手和TLS握手合并成一次交互。首次连接需要一次往返,之后的连接可以借助会话票据做到零往返(0-RTT),客户端在第一个包里就能携带业务请求。此外QUIC原生支持连接迁移,连接标识符与四元组解耦,客户端从Wi-Fi切到蜂窝网络时连接不会断,代理场景下这一点对移动端用户尤其有价值。
QUIC要求强制使用TLS 1.3,不存在明文模式。这一点对代理部署有直接影响:必须配置正式证书,自签名证书在客户端会直接握手失败。HTTP/3的流控、流量控制窗口等参数在Apache中可以通过指令微调,例如Protocols指令控制协议协商优先级。
Apache httpd编译并启用mod_http3
目前mod_http3还没有进入各主流发行版的默认软件仓库,需要从源码编译。httpd本身要先安装支持HTTP/3的依赖库,主要是ngtcp2和nghttp3这两套C库,前者负责QUIC传输层,后者负责HTTP/3语义层的帧解析。编译顺序是先装ngtcp2,再装nghttp3,最后编译httpd时加上--enable-http3参数。
# 安装ngtcp2 git clone https://github.com/ngtcp2/ngtcp2.git cd ngtcp2 && autoreconf -i && ./configure && make && make install # 安装nghttp3 git clone https://github.com/ngtcp2/nghttp3.git cd nghttp3 && autoreconf -i && ./configure && make && make install # 编译httpd,启用http3与代理缓存模块 ./configure --enable-http3 \ --enable-proxy --enable-proxy-http2 \ --enable-cache --enable-cache-disk \ --with-ssl --enable-ssl make && make install
编译完成后,在配置文件中启用模块并声明协议。这里要注意UDP 443端口必须开放,很多云服务器的安全组默认只放行TCP,这是上线后QUIC不通的最常见原因。证书建议使用包含完整链的pem文件,ECC证书在握手体积上更有优势。
LoadModule http3_module modules/mod_http3.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule cache_module modules/mod_cache.so
LoadModule cache_disk_module modules/mod_cache_disk.so
Protocols h3 h2 http/1.1
Listen 443 udp
Listen 443 tcp
<VirtualHost *:443>
ServerName proxy.ipipp.com
Protocols h3 h2
SSLEngine on
SSLCertificateFile /etc/httpd/certs/fullchain.pem
SSLCertificateKeyFile /etc/httpd/certs/privkey.pem
# 反向代理到后端
ProxyPass /api/ http://backend.internal:8080/
ProxyPassReverse /api/ http://backend.internal:8080/
</VirtualHost>Alt-Svc头与缓存策略配置
客户端怎么知道服务端支持HTTP/3?靠的是Alt-Svc响应头。当客户端先通过HTTP/2访问时,服务端在响应中声明自己还监听一个h3端口,浏览器记住后,下次访问会优先尝试QUIC,失败再回退到TCP。这个回退机制是HTTP/3部署安全性的关键,即使UDP被中间设备拦截,服务也不会不可用。
Header always set Alt-Svc 'h3=":443"; ma=86400'
缓存层面,mod_cache配合mod_cache_disk可以把后端响应缓存到本地磁盘,减少回源。配置时要注意QUIC下的缓存键与HTTP/2没有区别,都是基于请求URL和Vary头,所以缓存模块对协议透明。一个实践建议是只缓存幂等的GET响应,并对后端返回的Cache-Control头保持尊重,不要用CacheIgnoreNoStoreAct之类的指令强行覆盖。
CacheRoot /var/cache/httpd/proxy
CacheEnable disk /api/
CacheDirLevels 2
CacheDirLength 1
CacheMinFileSize 64
CacheMaxFileSize 5120000
CacheIgnoreNoStoreAct Off
<Location /api/static>
CacheDefaultExpire 3600
</Location>验证缓存命中可以观察X-Cache响应头,也可以用curl --http3配合-H 'Cache-Control: no-cache'做强制回源测试。日志中cache_disk的调试级别日志会输出键值和命中情况,排查问题时打开LogLevel cache_disk:debug即可。
与Istio网格的对接方案
Istio的 ingress 网关基于Envoy,Envoy本身对HTTP/3有原生支持,可以在网关层直接终结QUIC。如果Apache代理部署在网格外围,一种常见拓扑是:客户端通过QUIC连到Apache,Apache作为缓存和协议转换层,再以HTTP/2回源到Istio ingress网关,网格内部依旧走mTLS的HTTP/2。这种分层让QUIC只存在于公网边缘,内部链路保持稳定可控。
如果希望网格内部也跑HTTP/3,需要在Istio的Gateway资源中把protocol改为HTTP3,并确保网关Envoy监听UDP 443。目前Envoy对HTTP/3 upstream的支持仍在演进,上游连接建议保持HTTP/2,仅在边缘终结QUIC是更稳妥的选择。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: quic-gateway
namespace: edge
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https-quic
protocol: HTTP3
tls:
mode: SIMPLE
credentialName: edge-tls-cert
hosts:
- "api.ipipp.com"调试整条链路时,分段验证很重要。先用curl --http3-only确认客户端到Apache的QUIC握手成功,再在Apache上开启proxy:trace2日志观察回源请求,最后通过istioctl proxy-config listener检查网关监听器是否包含UDP 443。三段各自通了,整套HTTP/3代理缓存加网格的架构才算真正落地。