关键是要根据代理层级选择正确的Host头变量:若前端CDN透传原始域名,应使用proxy_set_header Host $http_x_forwarded_host;并同步更新proxy_ssl_name和proxy_cache_key中的租户标识,避免$host被覆盖导致SNI错配与缓存污染。

要让缓存回源准确识别原始租户域名,关键不是简单写一句 proxy_set_header Host $host,而是根据代理层级关系,明确哪一层该传什么、不能传什么——尤其当链路中存在 CDN、边缘网关或 Nginx 多级代理时,$host 很可能早已被覆盖为源站域名或泛用域名。
识别当前代理位置:Host 头在哪儿被改了
先确认你配置的 Nginx 是“最后一跳回源代理”还是“中间层”。常见情况:
- 如果你的 Nginx 直连多租户云存储(如对象存储 S3 兼容服务),且前端有 CDN(如 Cloudflare、阿里云全站加速),那么 CDN 通常已把原始
Host替换为它自己的源站地址(如origin.example.com) - 如果你的 Nginx 是 CDN 后的第一层代理,又再转发给另一台 Nginx 或负载均衡器,那
$host可能已是上一级代理设定的值,而非用户最初请求的租户域名 - 若流量未经过 CDN,而是客户端直连你的 Nginx,则
$host一般就是原始租户域名(如tenant-a.example.com),可直接使用
正确设置 Host 头:用对变量,不依赖默认
不要无条件写 proxy_set_header Host $host。应按实际来源选择更可靠的变量:
- 前端 CDN 明确透传原始 Host 时(如开启“保留原始 Host”或自动添加
X-Forwarded-Host),改用:proxy_set_header Host $http_x_forwarded_host; - 同时补发一条供后端日志或鉴权识别的头:
proxy_set_header X-Forwarded-Host $host; - 若 CDN 不支持透传,但你控制客户端或上游网关,可要求其在请求中携带自定义头(如
X-Tenant-Host),Nginx 中则用:proxy_set_header Host $http_x_tenant_host; - 绝对避免在多租户场景下硬编码固定值(如
proxy_set_header Host storage.tenant-a.com),否则无法复用配置
配合缓存键隔离租户资源
只修正 Host 头还不够,缓存必须按租户区分,否则 tenant-a.example.com/logo.png 和 tenant-b.example.com/logo.png 会互相覆盖:
- 确保
proxy_cache_key包含租户标识,典型写法:proxy_cache_key "$scheme$request_method$host$uri$is_args$args"; - 如果用了
$http_x_forwarded_host作为 Host 值,缓存键也应保持一致逻辑,避免键值与实际请求头错位 - 检查
proxy_ssl_name是否同步更新(如设为$http_x_forwarded_host),否则 TLS 握手仍会失败
验证是否生效的两个关键点
配置完成后,别只看响应状态码,重点验证底层行为:
- 用
openssl s_client -connect your-storage-endpoint:443 -servername tenant-a.example.com手动测试 SNI 是否能成功握手,确认证书 SAN 匹配 - 在云存储侧开启访问日志,检查收到的
Host请求头是否为你期望的租户域名,而非源站地址或泛域名


















