跳转到内容
Glossary 3 分钟阅读 841 次浏览

SNI(Server Name Indication,服务器名称指示)

了解 GProxy 如何在 TLS 连接中高效处理 Server Name Indication(SNI),通过其代理基础设施实现安全而精准的路由。

Security
SNI(Server Name Indication,服务器名称指示)

SNI(Server Name Indication)是一项 TLS 扩展,允许客户端在 TLS 握手过程中指明自己要访问的主机名,从而使代理能够为托管在同一个 IP 地址上的多个域名正确路由或终止 TLS 连接。

在 SNI 出现之前,如果同一台服务器上托管多个 HTTPS 网站,那么每张 SSL 证书都需要一个独立的 IP 地址。这个限制的根源在于:TLS 握手发生在 HTTP Host 头发送之前,服务器无从得知该出示哪张证书。RFC 6066 定义的 SNI 通过在 ClientHello 消息中携带目标主机名解决了这一问题,使服务器(或代理)能够选择正确的证书和配置。

SNI 的工作原理

客户端把目标主机名放在 ClientHello 消息内 server_name 扩展的 extension_data 字段中。这发生在任何加密数据交换之前,也在服务器出示证书之前。

ClientHello
  Version: TLS 1.2
  Random: ...
  Session ID: ...
  Cipher Suites: ...
  Extensions:
    ...
    Server Name (SNI)
      Server Name Type: host_name (0)
      Server Name: example.com
    ...

服务器或中间代理收到这个 ClientHello 后即可提取出 example.com 主机名。随后该信息被用于决定出示哪张 TLS 证书,以及如何路由或处理后续连接。

代理对 SNI 的处理

代理与 SNI 的交互方式取决于其类型、运行模式,以及它执行的是 TLS 终止还是透传。

透明代理

透明代理在客户端未显式配置使用它的情况下拦截流量。对于 TLS 流量,透明代理通常工作在第 4 层(TCP)。它一般把原始 TCP 流(包括携带 SNI 的 ClientHello 消息)直接转发给目标服务器。

  • SNI 可见性:透明代理可以检查 ClientHello 消息以提取 SNI 主机名。即使负载本身仍然加密,这也能实现基于目标域名的基本路由、日志记录或过滤规则。
  • TLS 终止:除非专门为深度包检测(DPI)或中间人(MITM)操作配置——这要求客户端信任代理的根 CA——透明代理通常不会为客户端终止 TLS。在标准透明部署中,代理把 ClientHello 传给源站服务器,由源站直接与客户端完成 TLS 握手。

正向代理

正向代理作为客户端向外部服务器请求资源时的中介。客户端会被显式配置为使用正向代理。

CONNECT 方法

对于 HTTPS 流量,客户端通常使用 HTTP CONNECT 方法,指示正向代理建立一条通往目标服务器的 TCP 隧道。

CONNECT example.com:443 HTTP/1.1
Host: example.com:443
Proxy-Connection: Keep-Alive

CONNECT 请求成功后(代理返回 HTTP/1.1 200 Connection established),客户端通过已建立的隧道直接与目标服务器发起 TLS 握手。

  • SNI 可见性(无拦截时):在这种模式下,正向代理通常不会处理 ClientHello 中的 SNI 值,因为它只是在转发原始的加密 TCP 流。目标主机名由 CONNECT 请求提供,代理据此进行路由。
  • TLS 终止:代理不终止 TLS;TLS 握手在客户端与源站服务器之间端到端完成。
TLS 拦截(MITM)

一些正向代理被配置为执行 TLS 拦截(也称 SSL 检查或 MITM 代理)。其做法是代理终止客户端的 TLS 连接,并向源站服务器另建一条新的 TLS 连接。

  1. 客户端与代理发起 TLS 握手:客户端向代理发送 ClientHello
  2. 代理提取 SNI:代理从 ClientHello 中读取 SNI 主机名。
  3. 代理生成证书:代理使用自己的 CA 证书(客户端已信任),为该 SNI 主机名动态生成一张服务器证书。
  4. 代理与客户端完成握手:代理把生成的证书出示给客户端。
  5. 代理与源站建立新的 TLS 连接:代理在自己的 ClientHello 中使用提取到的 SNI 主机名,与真实源站服务器发起新的 TLS 握手。
  6. 代理解密/重新加密流量:代理解密客户端流量、进行检查,然后在发往源站前重新加密,反向亦然。
  • SNI 的用途:SNI 对 TLS 拦截至关重要。代理使用 SNI 值来:
    • 确定对客户端冒充哪个主机名。
    • 确定向源站服务器请求哪个主机名。
  • 安全影响:要求客户端信任代理的根 CA。该模式让代理对加密流量拥有完全可见性。

反向代理

反向代理位于一台或多台 Web 服务器之前,把客户端请求导向合适的后端服务器。客户端连接的是反向代理,并不了解后端架构。

  • SNI 的用途:当客户端与反向代理发起 TLS 握手时,代理会收到包含 SNI 主机名的 ClientHello 消息。反向代理使用该 SNI 值来:
    • 在托管多个域名时,选择要出示给客户端的正确服务器证书。
    • 把请求路由到合适的后端服务器或应用池。
    • 应用域名专属策略(例如 WAF 规则、缓存、负载均衡)。
  • TLS 终止:反向代理几乎总是终止来自客户端的 TLS 连接。它们解密流量、进行处理,随后可能为与后端服务器通信而重新加密(重新加密,即后端 TLS)。

示例:带 SNI 的 Nginx 反向代理

http {
    # Default server for requests without SNI or for unknown SNI
    server {
        listen 443 ssl default_server;
        server_name _; # Catch-all
        ssl_certificate /etc/nginx/ssl/default.crt;
        ssl_certificate_key /etc/nginx/ssl/default.key;
        return 404;
    }

    # Backend for example.com
    server {
        listen 443 ssl;
        server_name example.com;
        ssl_certificate /etc/nginx/ssl/example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/example.com.key;
        location / {
            proxy_pass https://backend_example_com;
            proxy_ssl_server_name on; # Pass SNI to backend
        }
    }

    # Backend for api.example.com
    server {
        listen 443 ssl;
        server_name api.example.com;
        ssl_certificate /etc/nginx/ssl/api.example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
        location / {
            proxy_pass https://backend_api_example_com;
            proxy_ssl_server_name on; # Pass SNI to backend
        }
    }
}

在这份 Nginx 配置中,server_name 指令与客户端提交的 SNI 相匹配。Nginx 随后使用对应的 ssl_certificate,并把请求路由到指定的 proxy_pass 后端。proxy_ssl_server_name on 确保 Nginx 在与后端建立新的 TLS 连接时携带 SNI 主机名。

SNI 与四层、七层代理

第 4 层(TCP)代理与第 7 层(应用层)代理的区别也会影响 SNI 的处理方式。

  • 四层代理:工作在 TCP 层。它们可以检查 ClientHello 数据包以提取 SNI,而无需解密整条 TLS 流。这使得在 TLS 握手完成之前即可基于 SNI 做路由或过滤,且无需终止 TLS。TCP 模式下的 HAProxy 可以做到这一点。

    listen https_frontend bind *:443 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } tcp-request content track-sc0 src # Route based on SNI use_backend backend_example_com if { req_ssl_sni -i example.com } use_backend backend_api_example_com if { req_ssl_sni -i api.example.com } default_backend backend_default
    这份 HAProxy 配置使用 req_ssl_sni 检查 ClientHello 中的 SNI,并据此路由 TCP 连接,自身并不终止 TLS。

  • 七层代理:工作在应用层(例如 HTTP/HTTPS)。它们必须终止 TLS 才能访问应用层头部(如 HTTP Host、URL 路径等)。终止 TLS 时,它们使用最初 ClientHello 中的 SNI 选择正确的服务器证书,然后继续处理解密后的 HTTP 请求。反向代理通常是 L7 代理,执行 TLS 拦截的正向代理同样是 L7。

对比:代理的 SNI 处理模式

特性 透明代理(L4 透传) 正向代理(CONNECT,无 MITM) 正向代理(TLS 拦截/MITM) 反向代理(TLS 终止)
客户端 TLS 终止于 源站服务器 源站服务器 代理 代理
代理 TLS 终止于 不适用(透传) 不适用(透传) 源站服务器 不适用(若使用后端 TLS 则另建连接)
代理可见 SNI 是(ClientHello 中明文) 否(CONNECT 之后) 是(ClientHello 中明文) 是(ClientHello 中明文)
代理对 SNI 的用途 路由、日志、基础过滤 不直接来自 ClientHello 生成证书、选择源站 选择证书、路由、执行策略
代理是否解密流量
客户端信任要求 代理的根 CA 源站证书(由代理出示)

安全与隐私考量

SNI 在设计上以明文形式包含在 ClientHello 消息中传输。这意味着包括代理和 ISP 在内的网络中间方,即使在 TLS 通信其余部分已加密的情况下,仍能观察到客户端试图连接哪个主机名。

这种明文暴露带来隐私影响。为此,IETF 开发了 Encrypted SNI(ESNI),并正演进为 Encrypted Client Hello(ECH)。ESNI/ECH 会加密 ClientHello 消息中的 Server Name 扩展,使其对被动观察者不可读。

  • 对代理的影响:ESNI/ECH 会显著改变代理(尤其是 L4 代理,或在不完全终止 TLS 的情况下做 SNI 路由的代理)的工作方式。如果 SNI 被加密,代理就无法用它做明文路由决策。支持 ESNI/ECH 的代理需要参与密钥交换,或依赖其他机制(例如 IP 地址、DNS)完成初始路由;若自身就是目标 ECH 端点,则可解密 ECH。对于终止 TLS 的代理(如反向代理或 MITM 正向代理),它们仍需解密 ECH 才能获知目标主机名,用于选择证书和路由。
已更新: 04.03.2026
返回分类

试用我们的代理

遍布 100+ 国家的 20,000+ 代理

support_agent
GProxy Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.