必须同时启用 proxy_ssl_server_name on 和 proxy_ssl_name $host,否则 tls 握手因缺失或错配 sni 失败,导致 502 或 ssl_do_handshake() failed;$host 已标准化、不含端口、小写,是最稳妥的动态取值方式。

要让 Nginx 在代理到 HTTPS 后端时正确完成 TLS 握手,关键不是“开启开关”就完事,而是必须显式告诉它:**该向后端发哪个域名作为 SNI(Server Name Indication)值**——proxy_ssl_name 就是干这个的。它本身不生效,必须和 proxy_ssl_server_name on 配合使用,否则 TLS 握手会因缺失或错配 SNI 直接失败,返回 502 或 SSL_do_handshake() failed。
必须成对启用:proxy_ssl_server_name + proxy_ssl_name
proxy_ssl_server_name on 是开关,只决定“是否发送 SNI 字段”;proxy_ssl_name 才决定“字段里填什么”。两者缺一不可:
- 不加
proxy_ssl_server_name on:Nginx 根本不发 SNI,Client Hello 中无 server_name 扩展,多证书后端无法选证 - 只加
proxy_ssl_server_name on但不设proxy_ssl_name:Nginx 默认用proxy_pass中写的地址(如https://10.0.0.100或https://backend.internal)填 SNI,大概率与用户真实请求域名不一致 - 正确写法必须同时存在:
proxy_ssl_server_name on;<br>proxy_ssl_name $host;
推荐用 $host,而不是 $http_host 或硬编码
$host 是最稳妥的变量选择,它已自动标准化:小写、不含端口、优先取请求行 Host, fallback 到 server_name,语义清晰且兼容性好。
- 避免用
$http_host:可能带端口(如example.com:443),部分后端拒绝解析 - 禁用硬编码(如
proxy_ssl_name "api.example.com";):除非所有流量都指向同一租户/域名,否则必然错配 - 若后端依赖原始 Host(如 CDN 回源时覆盖了 Host 头),应改用
$http_x_forwarded_host,并确保上游透传:proxy_set_header X-Forwarded-Host $host;
配置位置与协议限制
这条规则有明确作用域,写错地方等于没配:
- 只能放在
location或server块中,不能写在upstream块里 - 仅对
proxy_pass https://...生效;对 HTTP 回源设置proxy_ssl_*指令完全无效 - 必须确保后端支持 SNI(现代网关、CDN、云服务基本都支持;老旧设备可能不兼容)
验证是否真正生效
配置写对 ≠ 实际发出。需确认 TLS 握手阶段 Client Hello 中的 SNI 值确实是目标域名:
- 临时开启 debug 日志:
error_log /var/log/nginx/error.log debug; - 触发一次请求后,在日志中搜索:
client sent server name或SSL_do_handshake - 也可用抓包工具(如 tcpdump + Wireshark)直接查看 Client Hello 的 SNI 字段内容











