必须开启proxy_ssl_server_name on并配合proxy_ssl_name动态设为$host(或cdn场景用$http_x_forwarded_host),同时proxy_set_header host保持一致,否则tls握手失败、502或证书不匹配。

要让 Nginx 在回源到 HTTPS 后端时正确发送 SNI(Server Name Indication),关键不是只打开 proxy_ssl_server_name,而是必须让它和真实请求意图对齐——即告诉上游“用户本来想访问哪个域名”。否则 TLS 握手失败、502、证书不匹配等问题会直接出现。
必须开启并放在正确作用域
proxy_ssl_server_name on; 是开关,不是默认行为。Nginx 默认关闭 SNI 扩展,即使 proxy_pass https://api.example.com,它也只解析出 IP,Client Hello 中不带 server_name 字段。
- 该指令必须写在
location或upstream块内,与proxy_pass处于同一配置层级 - 不能只写在
http块顶层,否则不生效 - 仅对
proxy_pass https://...生效;HTTP 回源时整套proxy_ssl_*都被忽略
必须配合 proxy_ssl_name 动态传值
proxy_ssl_server_name on 只启用 SNI 能力,真正决定发什么域名的是 proxy_ssl_name。
- 常规场景(如直连多域名后端):用
proxy_ssl_name $host;—— 它取自请求头 Host,已标准化、不含端口,最安全可靠 - CDN 回源场景(Host 被覆盖):CDN 通常把原始 Host 改成源站域名(如
origin.example.com),此时$host失真,应启用 CDN 的“透传原始 Host”功能,并改用proxy_ssl_name $http_x_forwarded_host; - upstream 负载均衡场景:Nginx 解析 upstream 得到 IP 后无法动态推导域名,
proxy_ssl_name必须是静态字符串,例如proxy_ssl_name "api.example.com";(注意引号)
配套 Host 头需保持一致
部分严格网关(如 Envoy、Istio sidecar、某些 ALB 配置)会校验 SNI 域名与 Host 请求头是否一致,不一致可能直接拒绝。
- 务必设置
proxy_set_header Host $host;(或$http_x_forwarded_host,视 CDN 场景而定) - 避免混用:比如 SNI 用
$host,但 Host 头写死为"api.example.com",容易触发校验失败 - 若后端需要固定 Host(如统一 API 网关),SNI 和 Host 都应设为相同静态值
验证是否真正生效
配置写对 ≠ 实际发出的 SNI 正确。建议用两种方式交叉验证:
- 在上游服务器(如另一台 Nginx)开启 debug 日志:
error_log /var/log/nginx/error.log debug;,触发请求后搜索日志中"client sent server name",确认输出域名是否符合预期 - 本地模拟握手:
openssl s_client -connect 上游IP:443 -servername your-domain.com,观察返回证书的 CN 或 SAN 是否匹配











