nginx需在tls握手阶段准确传递用户访问域名,须确保openssl支持sni、每个server块独立配置server_name与证书路径、反向代理时启用proxy_ssl_server_name并设proxy_ssl_name为$host或$http_x_forwarded_host,最后用openssl s_client验证sni是否生效。

核心是让 Nginx 在 TLS 握手阶段准确告诉上游(或本机监听端口)“用户真正要访问哪个域名”,而不是默认用 proxy_pass 地址或第一个 server 块的证书。SNI 不是开个开关就自动生效,必须配合域名传递和证书绑定才可靠。
确保 Nginx 和 OpenSSL 支持 SNI
Nginx 0.5.32+ 默认支持 SNI,但前提是编译时链接了支持 SNI 的 OpenSSL(不是 BoringSSL)。验证方法:
- 运行 nginx -V 2>&1 | grep -i openssl,确认输出含 OpenSSL 版本(如 OpenSSL 1.1.1w)
- 若无输出或显示 BoringSSL,需重装官方包或源码编译,启用 --with-openssl-opt="enable-tlsext"
每个 HTTPS server 块独立且精准匹配
不能共用证书路径,也不能省略 server_name。典型写法:
- listen 443 ssl http2; 必须显式声明 ssl
- server_name shop-a.com www.shop-a.com; 域名必须与证书 SAN 或 CN 完全一致
- ssl_certificate /path/to/shop-a.com/fullchain.pem; 和 ssl_certificate_key /path/to/shop-a.com/privkey.pem; 路径一一对应
- 未带 SNI 的旧请求会命中第一个定义的 server 块,建议把最常用站点放最前,或加 default_server 显式兜底
反向代理多证书后端时传对 SNI
当 Nginx 作为反向代理,上游是另一台 HTTPS 服务(如微服务、CDN 源站),仅靠 proxy_pass 不足以触发正确证书:
- 必须开启 proxy_ssl_server_name on;
- 设置 proxy_ssl_name $host;(推荐)——取自原始 Host 头,不含端口
- 若经 CDN 回源导致 Host 被覆盖(如变成 origin.example.com),改用 proxy_ssl_name $http_x_forwarded_host;,并确保 CDN 开启“透传原始 Host”
- 在 upstream 场景下,proxy_ssl_name 必须写字符串值,例如 proxy_ssl_name "api.example.com";,不可省略引号
快速验证 SNI 是否生效
不依赖浏览器,直接测 TLS 层:
- 用 OpenSSL 模拟握手:openssl s_client -connect your.ip:443 -servername shop-a.com -showcerts 2>/dev/null | openssl x509 -noout -subject
- 返回结果应为 CN=shop-a.com 或包含该域名的 subject
- 若日志可调,将 upstream 的 error_log 设为 debug 级,搜索 "client sent server name" 确认实际发出的 SNI 值











