SNI 是什么
SNI(Server Name Indication)是 TLS 握手中 ClientHello 携带的扩展:客户端在第一条消息里就把要访问的域名(明文)告诉服务器。它解决的是「一个 IP 上托管多个 HTTPS 站点时,服务器该出示哪张证书」——没有 SNI,服务器只能回默认证书,域名一多必然证书不匹配。该扩展由 RFC 3546 引入,现行规范是 RFC 6066,TLS 1.3 中依然保留(另有加密版本 ECH 在推进)。
基本信息
| 所在位置 | TLS ClientHello 中的 server_name 扩展 |
|---|---|
| 适用平台 | 所有现代浏览器、curl、OpenSSL 及 Nginx/Apache/HAProxy 等 |
| 规范 | RFC 6066 §3(原 RFC 3546) |
| 可见性 | 握手阶段明文传输,链路设备可解析 |
| 作用 | 服务器据此选择证书与虚拟主机 |
典型使用场景
- 同一台服务器用一张 IP 承载多个不同域名站点,按域名返回各自证书
- 排查「证书域名不匹配」:确认客户端是否真的发出了 SNI
- 老客户端或直接以 IP 访问时,验证默认站点会给出哪张证书
- 在无 DNS 环境用 --resolve 强制指定域名做 HTTPS 验证
- 分析链路设备是否按 SNI 做阻断或审计
带 SNI 与不带 SNI 对比
带 SNI 与不带 SNI 对比
# 带 SNI:服务器按域名给出对应证书
openssl s_client -connect 203.0.113.10:443 -servername www.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
# 不带 SNI:通常落到默认站点,证书可能与域名不符
openssl s_client -connect 203.0.113.10:443 </dev/null 2>/dev/null | openssl x509 -noout -subject
# 指定域名但不改 DNS,验证站点证书
curl -v --resolve www.example.com:443:203.0.113.10 https://www.example.com 2>&1 | grep -i -E 'subject|issuer'常见现象与归因
| 现象 | 原因 | 验证方法 |
|---|---|---|
| 同 IP 两个域名,只有一个证书报警 | 该域名不是默认 server,请求也没带 SNI | 加 -servername 指定域名复测是否恢复正常 |
| 浏览器正常但脚本请求失败 | 老版本客户端或直连 IP 时不发 SNI | 抓 ClientHello 看有无 server_name 扩展 |
| 特定域名握手被重置 | 中间设备按 SNI 关键字阻断 | 换出口线路或改用 IP 直连做对比 |
| 证书域名与访问域名不一致 | 默认证书配置错或虚拟主机顺序问题 | openssl x509 查看 SAN 列表 |
Nginx 中用 listen 443 ssl default_server 指定无 SNI 请求落到的默认站;建议把默认证书配成主站证书,避免直接访问 IP 时看到无关域名。
SNI 本身不加密也不认证:域名会被路径上的设备记录,隐私敏感场景要评估 ECH 等加密方案;同时不要把它当成任何安全控制手段。