根本原因是nginx未正确传递sni导致后端返回不匹配证书;需启用proxy_ssl_server_name on并动态设proxy_ssl_name $host,多域名或upstream场景须显式指定匹配证书san的域名。

这个问题本质不是证书本身错了,而是 Nginx 在 TLS 握手阶段没把客户端请求的域名准确告诉后端 HTTPS 服务,导致后端返回了不匹配的证书。关键在 SNI(Server Name Indication)是否传对、传准。
确认是 SNI 导致的证书不匹配
先看 Nginx 错误日志:/var/log/nginx/error.log 中搜这些关键词:
- SSL_do_handshake() failed
-
certificate verify failed(但你没开
proxy_ssl_verify on) - tlsv1 alert internal error
如果直接用 curl -v https://后端域名 能通,但走 Nginx 代理就 502 或报证书错,基本可以锁定是 SNI 传递问题。
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
必须启用并动态设置 SNI 值
Nginx 默认不发 SNI,必须显式开启,并让值随客户端 Host 头变化:
- 在对应
location块里加:proxy_ssl_server_name on; - 紧跟着加:
proxy_ssl_name $host;(推荐)
它会自动取 Host 请求头里的域名(已转小写、去端口) - 不能写
proxy_ssl_name $http_host;(可能含端口,Nginx 启动报错) - 也不能写死成
proxy_ssl_name "api.example.com";——除非所有请求都指向同一个后端域名
多域名共用一个 upstream 时要特别注意
如果用了 upstream 块且后端是 IP 或内部域名(如 gateway.internal:443),SNI 信息会丢失:
-
proxy_ssl_server_name on在 upstream 场景下无效 - 必须显式指定:
proxy_ssl_name "bucket-a.example.com";
值要用双引号包裹,且必须和后端证书的 SAN 完全一致(大小写敏感) - 泛域名证书(如
*.storage.example.com)就填具体子域,比如my-bucket.storage.example.com - 禁止写
proxy_ssl_name $host;——SSL 握手发生在变量解析前,Nginx 启动就报错
CDN 或多层代理场景要透传真实 Host
如果前面有 CDN、WAF 或其他反向代理,$host 可能已被覆盖为 CDN 域名:
- 改用:
proxy_ssl_name $http_x_forwarded_host; - 同时确保 CDN 正确设置了
X-Forwarded-Host头 - 验证方法:在 Nginx 配置中临时加
add_header X-Debug-Host $host;和X-Debug-XFH $http_x_forwarded_host;,用 curl 看响应头对比










