一台物理服务器或云实例通常只分配到一个公网 IP,却要同时托管多个独立域名服务。
传统 HTTP 虚拟主机靠 Host 头部区分站点,但 HTTPS 时代却面临致命矛盾:TLS 握手必须先于 HTTP 请求发生,服务器在收到 Client Hello 时根本不知道客户端想访问哪个域名,自然无法选择正确的 SSL/TLS 证书。
SNI(Server Name Indication,服务器名称指示) 正是解决 “一 IP 多证书” 的经典难题。它让客户端在 TLS 握手的第一步就把域名明文告诉服务器,从而实现一台 IP 上安全托管无数 HTTPS 网站。
SNI 已成为现代 Web 服务器的基石。如果没有 SNI,共享 IP 的多站点部署几乎不可能。本文将深度拆解握手矛盾、SNI 扩展原理、实际配置,以及旧版 Android 与 IE 的兼容性现状。
握手矛盾:为什么 TLS 握手前服务器不知道客户端请求哪个域名?
TLS 握手流程严格遵循“先加密,后传输”的原则:
1. TCP 三次握手建立连接(此时服务器只知道客户端 IP)。
2. 客户端发送 Client Hello(TLS 记录层第一条消息)。
3. 服务器必须立即返回 Server Hello + Certificate(包含证书链)。
4. 后续才进入 HTTP 层,客户端才会发送 Host: example.com 头部信息。
如果主机服务器上同时运行 example.com(证书 A)和 shop.example.com(证书 B),在第 3 步根本无法判断该发哪张证书。
早期两种解决方案
1. 每域名独占一个 IP(严重消耗IPv4 资源)。
2. 所有域名共用一张多域名证书(SAN 证书),但管理成本高、续期麻烦,且泛域名支持有限。
如果没有 SNI,多站点 HTTPS 就只能停留在“一个 IP 一个站点”的原始时代。
SNI 扩展原理:在 Client Hello 中包含域名信息
SNI 是 TLS 扩展协议(RFC 6066 定义),其原理:在 Client Hello 消息的 extensions 字段中,新增一个 server_name 类型(类型号 0),客户端直接把目标域名(hostname)填进去。
协议具体结构(简化 ASN.1)
Client Hello
extensions
server_name
name_type: host_name
host_name: "example.com"
服务器收到客户端 Client Hello 信息后,立即解析 SNI 扩展属性
1. 如果支持 SNI,则根据 host_name 查找对应的虚拟主机配置,选择正确的证书。
2. 如果客户端未发送 SNI,直接回退到默认证书(通常是第一个配置的站点)。
整个过程完全发生在 TLS 握手层,HTTP 层完全无感知。SNI 域名是明文传输的(直到 TLS 1.3 加密握手后消息才加密),这也是后来 ESNI/ECH(Encrypted Client Hello)要解决的隐私问题。
单IP多域名配置示例
1. Nginx 根据 SNI 中的 server_name 自动匹配虚拟主机,选择完全匹配的证书。
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
}
server {
listen 443 ssl http2;
server_name shop.example.com;
ssl_certificate /etc/ssl/shop.example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/shop.example.com/privkey.pem;
}
server {
listen 443 ssl http2;
server_name blog.example.com;
ssl_certificate /etc/ssl/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/blog.example.com/privkey.pem;
}
2. Apache、Caddy、Traefik 原理相同,只需在 VirtualHost 或 site 配置中声明 server_name。
兼容性:旧版 Android 与 IE 浏览器对 SNI 的支持现状
SNI 虽然早已标准化,但早期实现并不完美。现今主流浏览器已 100% 支持,但仍有极少数遗留客户端存在问题:
1. Internet Explorer:Windows XP 上的所有 IE 版本(IE6/7/8)完全不支持 SNI。Windows Vista 及以上 IE7+ 才支持。现在 Windows XP 全球占有份额已低于 0.01%,实际影响可忽略。
2. Android 原生浏览器:Android 2.x(Gingerbread 及以下)不支持 SNI。Android 3.0(Honeycomb)平板版及 4.x 手机版开始支持。现今 Android 2.x 设备占比接近 0,现代 App 均使用 HttpsURLConnection(自 Android 2.3 起支持 SNI)。
3. 其他老客户端(如 BlackBerry 旧版、Windows Mobile 6.5)同样不支持,但市场份额已趋近于零。
SNI 的长远意义与最佳实践
SNI 让共享 IP 的多站点 HTTPS 成为现实
1. 促使云主机、VPS、CDN 成本大幅下降。
2. Let's Encrypt 免费证书大规模普及(一个 IP 可签无数张)。
3. HTTP/2 + HTTP/3 多路复用在共享环境下的高效部署。
最佳实践
1. 所有单 IP 多虚拟主机配置必须显式声明 server_name 并绑定独立证书。
2. 使用 Let's Encrypt / CertificateHub 自动续期 + fullchain.pem。
3. 开启 TLS 1.3 + HSTS,结合 Encrypted Client Hello(ECH)进一步提升隐私。
4. 监控工具:Wireshark 过滤 tls.handshake.extensions_server_name 即可查看客户端发送的 SNI。
5. 高安全场景:配合 OCSP Stapling + Session Resumption,握手延迟再降 30ms。
SNI 是现代 Web 服务器的隐形英雄。用一个简单的 TLS 扩展,彻底打破了“一 IP 一证书”的枷锁,让无数中小网站、SaaS 平台、个人博客都能轻松拥有独立 HTTPS 体验。