Nginx反向代理HTTPS后端时必须启用SNI,否则TLS握手失败;需配置proxy_ssl_server_name on开启SNI发送能力,并用proxy_ssl_name $host动态传递客户端请求域名,确保后端能匹配正确证书。

核心在于让 Nginx 在连接后端 HTTPS 服务时,主动带上客户端请求的域名(SNI 字段),否则后端无法选出匹配证书,TLS 握手直接失败。
必须开启 proxy_ssl_server_name on
默认情况下,Nginx 用 proxy_pass 中写的地址(如 IP 或固定域名)作为 SNI 值发起 TLS 连接。这在单证书后端没问题,但面对一个 IP 托管多个域名、每个域名配独立证书的场景(如 CDN 回源、多租户网关、SaaS 后端),就会出错——后端收不到真实请求域名,只能返回默认证书或中断握手。
解决办法就是启用 SNI 扩展:
- 在
location或upstream块中添加proxy_ssl_server_name on; - 该指令本身不指定 SNI 内容,只打开“发送 SNI”这个能力
必须配合 proxy_ssl_name 动态传 Host
仅开启 SNI 不够,SNI 字段内容必须是用户原始请求的域名,否则仍会错配。这时要用 proxy_ssl_name 指定值:
- 推荐写成
proxy_ssl_name $host;:自动取请求头Host字段,已标准化(不含端口),最通用 - 若经 CDN(如 Cloudflare、阿里云全站加速)回源,CDN 可能覆盖了
Host头,此时需让 CDN 透传原始域名到X-Forwarded-Host,再配置为proxy_ssl_name $http_x_forwarded_host; - 避免硬编码如
proxy_ssl_name "api.example.com";:仅适用于单域名固定回源,多域名下必然失效
验证是否真正生效
配置写对不等于跑通,得确认 TLS 握手时发出的 SNI 值确实是预期域名:
- 在上游服务器(比如另一台 Nginx)开启 debug 日志:
error_log /var/log/nginx/error.log debug;,搜索日志中client sent server name条目,看值是否匹配 - 用 OpenSSL 模拟请求:
openssl s_client -connect 后端IP:443 -servername your-domain.com,观察返回证书的 CN 或 SAN 是否与该域名一致 - 注意:CDN 若实际走 HTTP 回源,
proxy_ssl_*指令完全不生效,先确认回源协议是 HTTPS
常见失效原因补充
即使配置看起来正确,也可能因底层依赖出问题:
- Nginx 编译时 OpenSSL 版本太低(需 ≥1.0.2h),运行时加载的也是旧版(可用
nginx -V | grep OpenSSL确认) - 后端服务本身不支持 SNI(如老旧 Nginx 或某些 LB),需升级或更换支持 SNI 的网关
- 证书绑定域名与 SNI 值不一致(比如 SNI 发了
shop-a.com,但后端该域名没配证书或证书已过期)


















