关键是要让Nginx将用户原始请求域名作为SNI值发送给后端云存储,需同时启用proxy_ssl_server_name on并动态设置proxy_ssl_name $host(或$http_x_forwarded_host),upstream场景则必须写死与证书SAN严格一致的字符串字面量。

要让 Nginx 正确代理到云端对象存储(如 S3 兼容网关、MinIO、Ceph RGW 或多租户 CDN 回源点),关键不是只开 proxy_ssl_server_name on,而是确保 TLS 握手时发送的 SNI 值等于用户原始请求的域名——否则后端无法识别租户、桶或客户身份,直接拒绝连接或返回错误证书,常见表现为 502、403 或 SSL certificate verify failed。
必须启用 SNI 并动态传真实域名
Nginx 默认不发 SNI,且即使开启也不自动用客户端 Host。需在 location 块中同时配置:
-
proxy_ssl_server_name on;—— 启用 SNI 扩展(仅对proxy_pass https://生效) -
proxy_ssl_name $host;—— 推荐使用$host,它已标准化为小写、无端口,比$http_host更可靠 -
proxy_pass https://storage-gw.internal;—— 必须用域名(而非 IP),若必须用 IP,则proxy_ssl_name不可省略且需配合变量
应对 CDN 或网关覆盖 Host 导致的 SNI 错配
当请求经过 Cloudflare、阿里云全站加速等 CDN 或 WAF 后再进 Nginx,它们常重写 Host 头为源站名(如 origin.example.com),导致 $host 失真:
安装并运行 Aperture(Lightning Labs 的 L402 闪电反向代理),用于创建 L402 付费墙、配置付费 API 端点、为其他代理托管付费内容或测试 L402 认证流程。
- 在 CDN 控制台开启“透传原始 Host”,通常会自动添加
X-Forwarded-Host请求头 - Nginx 中改用:
proxy_ssl_name $http_x_forwarded_host; - 同时补上:
proxy_set_header X-Forwarded-Host $host;,方便对象存储做租户路由或审计
upstream 场景下必须写死 SNI 字符串
若使用 upstream 定义多个网关节点(如负载均衡多个 MinIO 实例),Nginx 在 SSL 握手阶段无法解析变量,$host 会被忽略或报错:
upstream s3_backend { server gw-01.internal:443; server gw-02.internal:443; }- 在
location中必须写:proxy_ssl_name "bucket-a.example.com";(带双引号,字符串字面量) - 该值必须与后端证书的 SAN(Subject Alternative Name)完全一致,大小写敏感;泛域名证书(如
*.storage.example.com)则填具体子域(如my-bucket.storage.example.com)
验证 SNI 是否真正发出
配置写对 ≠ 实际生效。需确认 TLS 握手 Client Hello 中的 SNI 字段确实是目标域名:
- 在对象存储后端(如 MinIO debug 日志、RGW 日志或上游 Nginx)开启
error_log ... debug;,搜索client sent server name - 用 OpenSSL 手动模拟:
openssl s_client -connect storage-gw.internal:443 -servername bucket-a.example.com -showcerts,观察返回证书的 CN/SAN 是否匹配 - 对比相同请求走 Nginx 代理前后 SNI 值是否一致(可用
tcpdump+ Wireshark 查 Client Hello 的 Server Name 扩展)

















