必须启用proxy_ssl_server_name on并配合proxy_ssl_name $host,确保Nginx将客户端真实请求域名作为SNI发送给多租户后端,同时透传Host头以避免TLS握手失败或证书错配。

要让 Nginx 在代理到多租户 SaaS 环境时正确匹配后端证书,核心不是只打开 proxy_ssl_server_name,而是确保 TLS 握手时发送的 SNI 值,精准对应用户请求的租户域名——否则后端无法选出对应证书,TLS 握手失败或返回默认/错误证书。
必须启用 SNI 扩展并显式开启
Nginx 默认禁用 TLS 的 SNI 扩展(proxy_ssl_server_name off),即使 proxy_pass 指向 HTTPS 地址,Client Hello 里也不会携带 Server Name 字段。而多租户 SaaS 后端(如单 IP 托管 tenant-a.app.com 和 tenant-b.app.com)完全依赖该字段选证和路由。
- 在
location块中添加proxy_ssl_server_name on; - 该指令仅对
proxy_pass https://...生效,HTTP 回源无需配置 - 若使用
upstream,proxy_ssl_server_name必须写在引用它的location中,不能放在upstream定义内部
动态设置 proxy\_ssl\_name 为真实租户域名
仅开启 SNI 不够。Nginx 默认用 proxy_pass 中写的地址(如 https://10.0.0.100 或 https://api.internal)作为 SNI 值,这在多租户场景下必然错配。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 推荐配置
proxy_ssl_name $host;——$host已标准化(小写、不含端口),优先取请求行 Host,再 fallback 到 Host 请求头 - 避免写死
proxy_ssl_name "api.example.com";,除非所有流量都指向同一租户 - 若经 CDN 回源且原始 Host 被覆盖(如变成
origin.prod.cloud),需 CDN 开启“透传原始 Host”,并在 Nginx 改用proxy_ssl_name $http_x_forwarded_host;
保持 Host 请求头与 SNI 一致
SNI 是 TLS 层标识,Host 头是 HTTP 层标识。两者不一致会导致部分 SaaS 后端在 TLS 握手成功后,再根据 Host 头二次识别租户,引发逻辑错位(如登录跳转错误租户)。
- 务必添加
proxy_set_header Host $host; - 不要用
$http_host(可能含端口),也不要省略该头 - 建议一并透传
X-Real-IP、X-Forwarded-For,便于后端日志归因和租户级限流
验证 SNI 是否真正发出
配置完成不等于运行生效,必须确认实际握手阶段发出的 SNI 值是否符合预期:
- 在上游网关或后端 Nginx 上开启 debug 日志:
error_log /var/log/nginx/error.log debug;,搜索client sent server name - 用 OpenSSL 手动测试:
openssl s_client -connect 10.0.0.100:443 -servername tenant-a.app.com,观察返回证书的 CN 或 SAN 是否匹配 - 检查 Nginx 底层支持:
nginx -V | grep OpenSSL,确保 OpenSSL ≥ 1.0.2h 且编译时启用了enable-tlsext

















