必须启用proxy_ssl_server_name on并动态设置proxy_ssl_name $host(或$http_x_forwarded_host),确保sni携带用户真实请求域名,否则tls握手失败导致502/403;同时需proxy_set_header host $host保持tls层与http层一致。

要让 Nginx 在代理请求到多租户云环境(如 SaaS 后端、对象存储网关、共享 IP 的租户网关)时正确完成 TLS 握手并匹配证书,关键不是只打开 proxy_ssl_server_name on,而是确保它把用户实际访问的租户域名作为 SNI 值发给后端——否则后端无法选出对应证书,握手直接失败,常见表现为 502、403 或 SSL certificate verify failed。
必须启用 SNI 扩展并作用在正确位置
Nginx 默认不发送 SNI 字段,即使 proxy_pass 指向 HTTPS 地址,Client Hello 中也不会带 server_name 扩展。这会导致后端返回默认证书或拒绝连接。
- 在
location块中添加proxy_ssl_server_name on;—— 该指令只对proxy_pass https://...生效,HTTP 回源下无效 - 如果用了
upstream块定义后端,proxy_ssl_server_name必须写在引用它的location中,不能放在upstream内部 - 确认
proxy_pass是以https://开头,否则所有proxy_ssl_*指令均不生效
动态设置 proxy\_ssl\_name 确保 SNI 值准确
SNI 字段内容决定后端选哪张证书。默认它取 proxy_pass 中写的地址(如 https://10.0.0.100),这对多租户场景完全不可用。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 使用
proxy_ssl_name $host;——$host已标准化为小写、不含端口,且优先取请求行 Host,比$http_host更可靠 - 避免写死,例如
proxy_ssl_name "tenant-a.example.com";,除非所有流量都固定指向唯一租户 - 若前端有 CDN(如 Cloudflare、阿里云全站加速),它可能覆盖原始 Host 头,此时需在 CDN 控制台开启“透传原始 Host”,Nginx 中改用
proxy_ssl_name $http_x_forwarded_host;
同步透传 Host 请求头,保持 TLS 层与 HTTP 层一致
SNI 是 TLS 层标识,Host 头是 HTTP 层标识。两者不一致时,部分云平台会在 TLS 握手成功后,再依据 Host 头做租户路由,导致逻辑错位甚至 404。
- 加上
proxy_set_header Host $host;,确保后端收到的 Host 与 SNI 值一致 - 建议补充
proxy_set_header X-Forwarded-Host $host;,方便下游服务还原原始请求意图 - 若租户通过子域名识别(如
shop-a.example.com),可在server块中用正则捕获:server_name ~^(?<tenant>.+)\.example\.com$;</tenant>,再透传proxy_set_header X-Tenant-ID $tenant;
验证配置是否真正生效
配置写对不等于运行时发出正确的 SNI。必须通过日志或工具确认实际握手行为。
- 在上游服务(如另一台 Nginx 或测试 OpenSSL 服务器)开启 debug 日志:
error_log /var/log/nginx/error.log debug; - 发起一次请求后,搜索日志中的
client sent server name,确认输出的是shop-a.example.com这类真实租户域名,而非backend.internal或空值 - 用 OpenSSL 手动测试:
openssl s_client -connect 10.0.0.100:443 -servername shop-a.example.com,观察返回证书的 CN 或 SAN 是否匹配










