Nginx反向代理HTTPS后端需启用proxy_ssl_server_name on并配合proxy_ssl_name指定SNI域名,否则因默认禁用SNI导致多域名证书握手失败;还需同步设置proxy_set_header Host $host保持SNI与Host头一致。

要让 Nginx 在反向代理 HTTPS 后端时正确完成 TLS 握手,关键不是“开启某个开关”就完事,而是必须让后端服务器知道客户端真正想访问的是哪个域名——proxy_ssl_server_name 就是控制这个行为的指令,但它本身不决定传什么,只决定“是否传”。真正起作用的是它和 proxy_ssl_name 的配合。
为什么默认会握手失败
Nginx 默认关闭 SNI(Server Name Indication)扩展,即在与后端建立 TLS 连接时,Client Hello 中不携带 server_name 字段。这在以下场景会直接导致失败:
- 后端用单 IP 托管多个域名(如 tenant-a.example.com、api.b2b-saas.com),每个域名配了独立证书
- 后端是另一台 Nginx、Traefik、ALB 或 Istio 网关,依赖 SNI 选证或路由租户
- 即使跳过证书校验(
proxy_ssl_verify off),部分严格实现仍拒绝无 SNI 的握手
proxy_ssl_server_name 的作用范围和启用方式
这个指令只是“允许发送 SNI”,不是“自动发对的值”。它必须满足三个前提才生效:
- 只能出现在
location或server块中,不能写在upstream块里 - 仅对
proxy_pass https://...生效;对 HTTP 回源完全无效 - 必须显式写为
proxy_ssl_server_name on;,默认值是off
必须搭配 proxy_ssl_name 才能动态匹配
只开 proxy_ssl_server_name on 不够。Nginx 默认用 proxy_pass 中写的域名(比如 https://10.0.0.5 或 https://backend.internal)作为 SNI 值,这在多租户或 CDN 回源场景下必然错配。
所以一定要加:
-
proxy_ssl_name $host;—— 推荐,取自客户端请求行中的 Host,干净可靠 -
proxy_ssl_name $http_host;—— 可用,但可能含端口(如 example.com:443),某些后端不接受 - 避免写死
proxy_ssl_name "api.example.com";,除非所有请求都指向同一后端
别忘了同步透传 Host 请求头
很多后端不仅靠 SNI 选证,还靠 Host 头做业务路由(比如识别租户、分发到对应微服务)。如果 SNI 和 Host 不一致,部分网关(如 ALB、Istio sidecar)会直接拒绝请求。
因此需同时配置:
-
proxy_set_header Host $host;—— 保持与 SNI 域名一致 - 若上游依赖原始请求头,可加
proxy_set_header X-Forwarded-Host $host;
upstream 场景下的特别处理
如果你用了 upstream 块做负载均衡(例如对接多个 API 实例),注意:proxy_ssl_server_name on 仍需写在 location 中,且必须额外指定 proxy_ssl_name:
upstream api_backend { server api.example.com:443; }location /api/ { proxy_pass https://api_backend/; proxy_ssl_server_name on; proxy_ssl_name "api.example.com"; proxy_set_header Host "api.example.com"; }
这里 proxy_ssl_name 必须是字符串字面量(带引号),不能用变量——因为 upstream 解析后只剩 IP,Nginx 无法在运行时从变量推导出应发哪个 SNI。


















