必须同时启用 proxy_ssl_server_name on 并设置 proxy_ssl_name,否则上游 HTTPS 服务无法选择正确证书,导致 TLS 握手失败或返回默认证书;proxy_ssl_server_name 仅启用 SNI 扩展,proxy_ssl_name 才决定实际发送的域名值,二者缺一不可。

当 Nginx 作为反向代理访问 HTTPS 上游服务时,TLS 握手默认使用 proxy_pass 指令中配置的域名(或 IP)作为 SNI 主机名。但若上游是泛域名证书、多租户共享 IP 的服务,或你希望强制指定与证书匹配的主机名(比如 api.example.com 而非 10.0.1.5),就需要用 proxy_ssl_name 显式覆盖 SNI 名称。
为什么需要 proxy_ssl_name?
SSL/TLS 握手阶段需发送 SNI(Server Name Indication)扩展,告诉上游服务器“我想访问哪个虚拟主机”。Nginx 默认把 proxy_pass 的 host 部分(如 https://backend.internal 中的 backend.internal)作为 SNI 值;但如果 proxy_pass 写的是 IP 地址(如 https://10.0.1.5:443),SNI 就会为空,导致上游无法正确选择证书,握手失败或返回默认证书——这在私有云、K8s Ingress 后端、或对接 Cloudflare Tunnel 等场景很常见。
基本用法:静态指定主机名
在 location 或 upstream 块中添加 proxy_ssl_name,值为你要声明的 SNI 主机名(必须与上游证书的 CN 或 SAN 匹配):
location /api/ {
proxy_pass https://10.0.1.5:443/;
proxy_ssl_name "api.example.com";
proxy_ssl_server_name on; # 必须启用,否则 proxy_ssl_name 不生效
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/certs/upstream-ca.crt;
}-
proxy_ssl_server_name on是前提,它告诉 Nginx 在 TLS 握手中发送 SNI 扩展 -
proxy_ssl_name值支持变量(见下文),但不可含空格或非法字符 -
proxy_ssl_verify on和可信 CA 配置建议同步开启,避免证书校验绕过
动态设置:用变量适配不同后端
若一个 location 需根据请求头、路径或 upstream 分组动态切换 SNI 名称,可用 Nginx 变量:
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
map $host $upstream_sni {
~^app1\.example\.com$ "api-v1.app1.example.com";
~^app2\.example\.com$ "api-v2.app2.example.com";
default "default.api.example.com";
}
<p>server {
listen 443 ssl;
server_name app1.example.com app2.example.com;</p><pre class="brush:php;toolbar:false;">location / {
proxy_pass https://10.0.1.5:443;
proxy_ssl_name $upstream_sni;
proxy_ssl_server_name on;
# 其他 SSL 设置...
}}
-
map指令提前定义变量映射关系,性能高效且可读性强 - $http_host、
$arg_service或自定义 header,例如:proxy_ssl_name $http_x-upstream-sni; - proxy_pass 执行前已确定,不能依赖 rewrite 后的 URI 变量(除非用
rewrite ... break;)
常见问题与验证方法
配置后若仍报 SSL_do_handshake() failed 或证书不匹配,可这样排查:
- openssl s_client -connect 10.0.1.5:443 -servername api.example.com 手动测试 SNI 是否被上游接受
- nginx debug>error_log /var/log/nginx/error.log debug;,搜索
SSL_do_handshake和sni关键字 - proxy_ssl_server_name on 出现在
proxy_ssl_name之前(顺序无关,但习惯上放前面) - keepalive,注意连接复用时 SNI 是首次握手决定的,后续请求沿用——所以
proxy_ssl_name应保持稳定










