SNI(Server Name Indication)
SNI 是 TLS ClientHello 中用于声明目标主机名的扩展,主要解决同一 IP 承载多个 TLS 虚拟主机时的证书与站点选择问题;传统 SNI 可被链路旁观者看到,ECH 用于保护这类 ClientHello 元数据。
[!info] related notes
- 所属 MOC: 计算机网络 MOC
- 前置概念: TLS 握手(TLS 1.3 主线)、HTTPS
- 并列概念: ECH
- 易混淆概念: VLESS
- 关系笔记: REALITY、REALITY、Cloudflare 与 fallback 流量滥用
SNI(Server Name Indication)
一句话定义
SNI(Server Name Indication,服务器名称指示)是 TLS ClientHello 中的一个扩展,用来告诉 TLS 服务端“本次握手希望访问哪个主机名”。它最初解决的是一个 IP 同时承载多个 HTTPS/TLS 虚拟主机时,服务端如何在握手早期选择正确证书和站点配置的问题。
为什么 TLS 需要 SNI
一台服务器或 CDN 边缘节点常常同时承载许多域名:
203.0.x.x
├── a.example
├── b.example
└── c.example
TLS 证书在 HTTP 请求发送之前就需要确定,因此客户端会在 ClientHello 中携带类似:
SNI = b.example
服务端据此选择:
- 对应证书;
- 对应 TLS 配置;
- 对应虚拟主机或后续路由策略。
RFC 6066 将这一能力标准化为 server_name 扩展。
SNI 在连接中的位置
典型 HTTPS/TCP 链路可以简化为:
DNS
→ 得到目标 IP
→ TCP 连接到 IP:443
→ TLS ClientHello
└── SNI = hostname
→ TLS 握手完成
→ 加密后的 HTTP
└── Host / :authority
因此需要区分三个概念:
| 字段 | 解决的问题 |
|---|---|
连接目标 IP / server | TCP/QUIC 实际先连接到哪台网络端点 |
SNI / server_name | TLS 握手希望访问哪个主机名 |
HTTP Host / :authority | HTTP 层请求哪个虚拟主机 |
正常浏览网页时三者经常指向同一站点,因此容易被误认为是同一个东西;在 CDN、反向代理、优选 IP、代理协议和 REALITY 场景中,它们可以不同。
为什么传统 SNI 会暴露
TLS 1.3 已经加密握手中的大部分内容,但传统 ClientHello 本身仍需要先发送;未启用 ECH 时,其中的 SNI 可以被链路旁观者观察到,因此会泄露“客户端正在请求哪个主机名”这一元数据。
这并不等于 HTTPS 内容没有加密:
旁观者可能看到:SNI = example.com
旁观者通常看不到:HTTP 路径、Cookie、请求体、响应正文
具体可见范围还受 TLS 版本、ECH、代理、DNS、QUIC 等因素影响。
ECH 如何改变这一点
ECH 会把真正的 ClientHello 作为 ClientHelloInner 加密,只暴露一个外层 ClientHelloOuter,从而保护 SNI 等敏感字段。
因此“HTTPS 一定会明文暴露 SNI”已经不是永恒规则,更准确的表述是:
传统未启用 ECH 的 TLS ClientHello 会暴露 SNI;启用并成功协商 ECH 后,真实 SNI 可以被保护。
SNI 与代理伪装
SNI 本身不是为了代理伪装设计的。它是 TLS 的虚拟主机扩展。
某些代理/传输安全方案会利用“外界能够观察 SNI 和 TLS 握手外观”这一事实,让代理连接表现得更接近正常 TLS 流量。例如 REALITY 会借用目标站点的 TLS 外观与握手特征;此时客户端连接的服务器 IP、SNI 和代理隧道中的最终访问目标可能完全不同。
典型的 REALITY + VLESS 场景:
外层连接:
server = VPS_IP
SNI = camouflage.example
内层代理请求:
destination = youtube.com:443
SNI 只描述外层 TLS/REALITY 握手的主机名,不等于代理用户最终访问的网站。
常见误解
SNI 就是最终访问目标
不是。对普通 HTTPS,它通常与目标站点一致;对代理/CDN/反代场景,它可能只是外层握手使用的主机名。
只要把 SNI 写成大厂域名,就一定会被路由到自己的代理
不是。SNI 会影响 TLS 虚拟主机、证书或边缘路由,但“最终如何到达代理服务”还依赖 IP、CDN、Host、REALITY、反代规则等完整链路。
SNI 本身负责加密
不是。SNI 是 TLS 扩展字段;TLS 的机密性来自密钥协商和记录层保护。ECH 才专门用于保护 ClientHello/SNI 等握手元数据。
学习检查
- SNI 的全称是什么,最初解决什么问题?
- 为什么一台 IP 承载多个 HTTPS 站点时,需要在 HTTP 请求之前知道主机名?
server/IP、SNI 与 HTTP Host 分别属于哪一层?- 为什么传统 TLS 1.3 仍可能泄露 SNI?
- ECH 保护的是哪一部分信息?
- 为什么代理场景中的 SNI 不一定等于最终访问网站?
参考
- RFC 6066 — TLS Extensions: Server Name Indication: https://www.rfc-editor.org/rfc/rfc6066.html
- RFC 9849 — TLS Encrypted Client Hello: https://www.rfc-editor.org/rfc/rfc9849.html