silence
发布于 2026-07-02 / 27 阅读
0
0

四层代理对chrome浏览器"多域名 HTTP/2 连接合并"的影响

在chrome浏览器中有一个机制“Connection Coalescing”,它将发往不同域名(Host)的请求复用到同一条 TCP 连接上,判断条件如下:

IP 地址必须相同

证书必须覆盖所有请求域名(证书的Subject Alternative Name或者common name)

同类的域名请求都必须是https访问

例:现有业务域名 a.silenceweb.cn b.silenceweb.cn,他们部署在各自的后端服务上,但他们使用相同的ssl证书,同时支持http/2(或先被访问的域名支持http/2)。

当chrome发起a域名的http/2连接时,会产生一个socket、http2_session。

当chrome的另一个页签向b域名发起https连接时,会复用a域名的socket、http2_session。

但如果是在a域名的页签html代码中调用b域名,则不会复用,会新起一个连接。(忽略协议 http/1.1,为了测试我停用了app server b的h2,实际无论是哪种协议都会这样)

没错就是这样反直觉,将在后面给出实验记录。

这个机制可能会引发什么问题呢?

当我们在网络链路中插入4层代理时可能会导致域名请求无法转发到对应的服务上。

链路如下:client -> proxy server -> app server

当首次访问域名a时:

client上的代理配置使域名a的dns解析结果指向代理服务器;(通常将所有需要四层代理的域名都指向 proxy server)

client与proxy server建立tcp连接;

cilent向proxy server发送client hello报文

proxy server抽取client hello报文中的SNI用于解析app server ip;

proxy server与app server建立tcp连接,开始转发4层以上的报文,包括tls握手报文。

后续该条连接进来的所有数据都转发给这个app server a,即使请求的http.heders.host已经发生变化,由于是四层代理无法感知。

如果域名a以外的业务域名没有在这个app server a上部署,将无法正常获取到响应。

典型的nginx stream代理配置如下:

stream {
    resolver 8.8.8.8 valid=300s ipv6=off;
    resolver_timeout 5s;

    server {
        listen 443;
        # 开启 SNI 预读,获取客户端 TLS 握手中的域名
        ssl_preread on;

        # 使用 SNI 域名和当前监听端口作为转发目标
        # 因为变量参与,nginx 会通过 resolver 实时解析该域名
        proxy_pass $ssl_preread_server_name:$server_port;

        # 可选:超时等参数
        proxy_connect_timeout 10s;
    }
}

实验信息记录

证书信息

自签的证书需要安装到操作系统的受信证书,否则浏览器不会使用该机制

net-export-log

使用浏览器的工具 net-export 导出网络日志后再使用 https://netlog-viewer.appspot.com/ 工具格式化文件。

HTTP/2 session统计

可见域名a、b在同一个会话中,点击ID可以进入Events详情

Events详情

HTTP2_SESSION ID: 4587 它使用的 SOCKET ID 4581

会话中对域名a的请求

会话中对域名b的请求

http业务异常

由于域名b的业务部署在app server b,但域名b的请求被proxy server转发给了app server a,所有无法得到正确响应。

代理服务器tcpdump包

握手过程


评论