nginx原生支持sni,需满足监听443 ssl、配置server_name与证书域名一致、独立设置ssl_certificate及key;反向代理时须启用proxy_ssl_server_name on和proxy_ssl_name;cdn场景下应使用$http_x_forwarded_host并开启原始host透传。

Nginx 原生支持 SNI,无需额外模块,但必须满足两个前提:启用 HTTPS 监听,且配置了至少一个有效的 SSL 证书。SNI 不是 Nginx 的可选功能,而是 TLS 协议层的机制——只要客户端在 ClientHello 中携带 server_name 扩展,Nginx 就会自动解析并据此选择匹配的证书。
SSL 配置中 SNI 自动生效的条件
只要以下三点同时满足,SNI 就会正常工作:
- server 块监听 443 端口并启用 ssl(如 listen 443 ssl)
- 每个 server 块明确声明 server_name,且与证书中的域名一致(CN 或 SAN)
- 每个 server 块独立配置 ssl_certificate 和 ssl_certificate_key
例如,同一 IP 上托管 shop-a.com 和 api.example.com,只需分别定义两个 server 块,各自绑定对应证书,Nginx 在 TLS 握手阶段就能根据 SNI 值精准分发证书,无需任何额外指令。
反向代理场景下 SNI 必须手动开启
当 Nginx 作为反向代理(proxy_pass 到后端 HTTPS 服务)时,它默认不会把客户端的 SNI 透传给上游。此时需显式启用:
- proxy_ssl_server_name on; —— 打开透传开关
- proxy_ssl_name $host; —— 指定发送的 SNI 值(推荐用 $host,已标准化为小写、无端口)
这两行必须放在 location 块内,不能写在 upstream 里。若后端使用固定域名(如 gateway.internal),则 proxy_ssl_name 应写死为 "gateway.internal",且必须加英文双引号。
CDN 或中间网关导致 SNI 错配的应对方法
Cloudflare、阿里云全站加速等常重写 Host 头,使 $host 变成源站名而非用户原始域名,进而导致 proxy_ssl_name 发送错误 SNI。解决办法是:
- 在 CDN 控制台开启「透传原始 Host」或类似选项
- 确保 CDN 添加 X-Forwarded-Host 请求头
- Nginx 中改用:proxy_ssl_name $http_x_forwarded_host;
- 同时建议添加:proxy_set_header X-Forwarded-Host $host;,便于后端识别真实域名
验证 SNI 是否真正按预期发出
配置正确 ≠ 实际生效。最可靠的方式是抓包确认:
- 在 Nginx 机器运行:tcpdump -i any port 443 -w sni.pcap
- 用 Wireshark 打开,过滤 tls.handshake.type == 1
- 展开 Client Hello → Extensions → server_name → Server Name Indication,查看值是否为你期望的域名
如果该字段为空或值不符,说明 SNI 未传递成功,需回溯检查 proxy_ssl_server_name / proxy_ssl_name 配置位置及变量取值逻辑。











