Nginx proxy_ssl_name 在多证书后端回源时,如何配置实现正确域名解析?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1049个文字,预计阅读时间需要5分钟。
很抱歉,您提供的文本似乎不完整,我无法直接进行改写。请提供完整的文本内容,以便我能够根据您的要求进行改写。
为什么 proxy_ssl_name 必须配合 proxy_ssl_server_name on 才生效
默认情况下,Nginx 与 HTTPS 后端建连时根本不会发送 SNI 字段——proxy_ssl_server_name 默认是 off。哪怕你写了 proxy_ssl_name $host,只要没开这个开关,SNI 就不会出现在 TLS ClientHello 里,上游压根收不到域名信息。
开启后,Nginx 才会调用 SSL_set_tlsext_host_name() 把 proxy_ssl_name 的值塞进 SNI 扩展。所以这两条必须成对出现:
-
proxy_ssl_server_name on;—— 启用 SNI 发送能力 -
proxy_ssl_name $host;—— 指定要发哪个域名(推荐用$host,已标准化、不含端口)
单独写其中一条,等于没配。
proxy_ssl_name 写死 vs 动态变量:什么时候能硬编码
写死 proxy_ssl_name "api.example.com"; 只在一种场景安全:所有请求都指向同一后端域名,且该域名在上游证书的 SAN 中明确存在。一旦后端是多租户架构(比如 shop-a.com、shop-b.com 共用一个 IP),就必须用变量。
常见错误写法:
-
proxy_ssl_name "backend.internal";—— 但用户请求的是shop-a.com,证书 SAN 是shop-a.com,校验直接失败 -
proxy_ssl_name $http_host;—— 若客户端带端口(如shop-a.com:8443),SNI 传过去可能被上游拒绝(部分服务不接受带端口的 SNI)
正确做法始终优先选 $host,它由 Nginx 自动剥离端口,只保留纯域名。
CDN 回源时 $host 被覆盖,如何让 SNI 仍匹配原始请求
Cloudflare、阿里云全站加速等 CDN 默认会把回源 Host 头强制改成你在控制台配置的“源站域名”,导致 $host 变成 origin.example.com,而非用户实际访问的 shop-a.com。此时 SNI 错配,握手失败。
解决路径很明确:
- 在 CDN 控制台开启「透传原始 Host」或类似功能(通常表现为传递
X-Forwarded-Host头) - Nginx 中改用
proxy_ssl_name $http_x_forwarded_host; - 同时加一句
proxy_set_header X-Forwarded-Host $host;,方便后端日志和路由逻辑识别真实域名
注意:$http_x_forwarded_host 是小写 + 下划线命名,不能写成 $http_X_Forwarded_Host 或其他变体,Nginx 不识别。
proxy_ssl_verify 开启后 handshake 失败,大概率是 proxy_ssl_name 不匹配
启用 proxy_ssl_verify on 后报 SSL_do_handshake() failed 或 certificate verify failed,90% 以上不是证书链问题,而是 proxy_ssl_name 值跟证书 SAN 对不上。
验证逻辑是:Nginx 拿 proxy_ssl_name 的值去比对上游证书的 Subject Alternative Name(优先)或 Common Name。如果 proxy_pass 写的是 IP 地址(如 https://10.0.0.100),而证书里没有 10.0.0.100 这个 SAN,就必须显式设 proxy_ssl_name 为证书里实际存在的域名。
调试建议:
- 用
openssl s_client -connect 10.0.0.100:443 -servername shop-a.com手动测试,确认上游是否真能返回匹配证书 - 检查 Nginx error log,搜索
SSL certificate problem,看具体提示哪个域名不匹配 - 确保
proxy_ssl_name和proxy_ssl_server_name on在同一个作用域(location或upstream块内)
最易忽略的一点:SNI 匹配发生在 TLS 握手最开始,早于 HTTP 请求头解析;所以 proxy_ssl_name 的取值时机非常靠前,任何依赖后续 rewrite 或 set 指令生成的变量,都无法用于此处。
本文共计1049个文字,预计阅读时间需要5分钟。
很抱歉,您提供的文本似乎不完整,我无法直接进行改写。请提供完整的文本内容,以便我能够根据您的要求进行改写。
为什么 proxy_ssl_name 必须配合 proxy_ssl_server_name on 才生效
默认情况下,Nginx 与 HTTPS 后端建连时根本不会发送 SNI 字段——proxy_ssl_server_name 默认是 off。哪怕你写了 proxy_ssl_name $host,只要没开这个开关,SNI 就不会出现在 TLS ClientHello 里,上游压根收不到域名信息。
开启后,Nginx 才会调用 SSL_set_tlsext_host_name() 把 proxy_ssl_name 的值塞进 SNI 扩展。所以这两条必须成对出现:
-
proxy_ssl_server_name on;—— 启用 SNI 发送能力 -
proxy_ssl_name $host;—— 指定要发哪个域名(推荐用$host,已标准化、不含端口)
单独写其中一条,等于没配。
proxy_ssl_name 写死 vs 动态变量:什么时候能硬编码
写死 proxy_ssl_name "api.example.com"; 只在一种场景安全:所有请求都指向同一后端域名,且该域名在上游证书的 SAN 中明确存在。一旦后端是多租户架构(比如 shop-a.com、shop-b.com 共用一个 IP),就必须用变量。
常见错误写法:
-
proxy_ssl_name "backend.internal";—— 但用户请求的是shop-a.com,证书 SAN 是shop-a.com,校验直接失败 -
proxy_ssl_name $http_host;—— 若客户端带端口(如shop-a.com:8443),SNI 传过去可能被上游拒绝(部分服务不接受带端口的 SNI)
正确做法始终优先选 $host,它由 Nginx 自动剥离端口,只保留纯域名。
CDN 回源时 $host 被覆盖,如何让 SNI 仍匹配原始请求
Cloudflare、阿里云全站加速等 CDN 默认会把回源 Host 头强制改成你在控制台配置的“源站域名”,导致 $host 变成 origin.example.com,而非用户实际访问的 shop-a.com。此时 SNI 错配,握手失败。
解决路径很明确:
- 在 CDN 控制台开启「透传原始 Host」或类似功能(通常表现为传递
X-Forwarded-Host头) - Nginx 中改用
proxy_ssl_name $http_x_forwarded_host; - 同时加一句
proxy_set_header X-Forwarded-Host $host;,方便后端日志和路由逻辑识别真实域名
注意:$http_x_forwarded_host 是小写 + 下划线命名,不能写成 $http_X_Forwarded_Host 或其他变体,Nginx 不识别。
proxy_ssl_verify 开启后 handshake 失败,大概率是 proxy_ssl_name 不匹配
启用 proxy_ssl_verify on 后报 SSL_do_handshake() failed 或 certificate verify failed,90% 以上不是证书链问题,而是 proxy_ssl_name 值跟证书 SAN 对不上。
验证逻辑是:Nginx 拿 proxy_ssl_name 的值去比对上游证书的 Subject Alternative Name(优先)或 Common Name。如果 proxy_pass 写的是 IP 地址(如 https://10.0.0.100),而证书里没有 10.0.0.100 这个 SAN,就必须显式设 proxy_ssl_name 为证书里实际存在的域名。
调试建议:
- 用
openssl s_client -connect 10.0.0.100:443 -servername shop-a.com手动测试,确认上游是否真能返回匹配证书 - 检查 Nginx error log,搜索
SSL certificate problem,看具体提示哪个域名不匹配 - 确保
proxy_ssl_name和proxy_ssl_server_name on在同一个作用域(location或upstream块内)
最易忽略的一点:SNI 匹配发生在 TLS 握手最开始,早于 HTTP 请求头解析;所以 proxy_ssl_name 的取值时机非常靠前,任何依赖后续 rewrite 或 set 指令生成的变量,都无法用于此处。

