一次讲清:反向代理里的 Host、SNI 与证书
它们都和域名有关,却发生在连接的不同阶段。把顺序理清,许多“证书明明没问题”的故障会突然变得简单。
浏览器访问 https://notes.example.com 时,域名至少会出现三次:先用于 DNS 查询,再进入 TLS 握手中的 SNI,最后进入 HTTP 请求里的 Host。它们看起来相同,但服务对象并不相同。
最实用的判断方式不是背定义,而是问:此刻连接是否已经加密?
先后顺序决定职责
DNS 把名称解析为地址。客户端连上目标地址后,在 TLS 握手阶段发送 SNI,服务器据此选择证书和 TLS 配置。握手完成、加密通道建立以后,HTTP 请求才会携带 Host 头,反向代理再据此选择上游。
DNS: notes.example.com → 203.0.113.10
TCP: client → 203.0.113.10:443
TLS: SNI = notes.example.com
HTTP: Host = notes.example.com这也解释了一个常见误区:如果证书在握手阶段已经选错,修改 HTTP 层的 Host 不会修复它,因为错误发生得更早。
三个概念分别回答什么问题
- SNI:同一个 IP 和端口上,该使用哪一套 TLS 配置与证书?
- 证书:服务器声明的身份是否覆盖当前域名,签发链是否可信,是否仍在有效期?
- Host:解密后的 HTTP 请求应该交给哪个虚拟主机或上游服务?
多数配置中 SNI 与 Host 值相同,于是边界被隐藏了。排障时可以故意把它们拆开观察。
一条可重复的检查路径
先用 dig 确认解析,再用 OpenSSL 显式指定 SNI 检查证书,最后用 curl 固定解析并观察 HTTP 响应:
dig +short notes.example.com
openssl s_client \
-connect 203.0.113.10:443 \
-servername notes.example.com
curl -v --resolve notes.example.com:443:203.0.113.10 \
https://notes.example.com/--resolve 很有用:它让 URL 中的域名保持不变,因此 SNI 和 Host 仍然正确,同时绕过本地 DNS 指向指定地址。这样能把“解析错误”和“服务端配置错误”分开。
反向代理中最容易漏掉的边界
第一段 TLS 可能终止在反向代理;如果代理再以 HTTPS 访问上游,还会发生第二次 TLS 握手。此时客户端的 SNI、代理访问上游时的 SNI,以及两段连接校验的证书,属于两套独立关系。
所以排障记录里最好明确写“客户端到代理”还是“代理到上游”。只写“HTTPS 不通”,通常会让接手的人从错误的一段开始查。
小结
记住时间线即可:DNS 找地址,SNI 在加密前帮助选择证书,证书证明身份,Host 在加密后帮助选择 HTTP 站点。沿连接顺序检查,比反复重载配置有效得多。