Nginx反向代理多级CDN时403错误可能由SNI丢失导致:proxy_pass若用IP则SNI为空,需改用域名并启用proxy_ssl_server_name on;多级场景下可用proxy_ssl_name分离SNI与Host头,确保匹配CDN校验要求。

这个问题其实不常见,但一旦出现就容易卡住——Nginx 反向代理后端是多级 CDN(比如 Cloudflare → 自建 CDN → 源站),而源站或中间 CDN 启用了 SNI 强校验,但 Nginx 默认发起上游连接时未透传或未正确设置 SNI,导致 SSL 握手失败,最终表现为 403(部分 CDN 在 SNI 不匹配时直接拒接、返回 403 而非 421 或 TLS alert)。
确认是否为 SNI 丢失引发的 403
先排除其他常见 403 原因(如权限、index 缺失、Host 头不匹配等)。重点验证:
- 用 curl -v --resolve 直连目标 CDN 域名和 IP,看是否同样 403;若直连正常,说明问题出在 Nginx 代理链路中
- 抓包分析 Nginx 到上游的 TLS 握手:用 tcpdump -i any port 443 -w nginx_upstream.pcap,然后 Wireshark 打开,查看 Client Hello 中的 Server Name Indication 字段是否为空或错误
- 检查 Nginx error log 是否有类似 "SSL_do_handshake() failed" 或 "no suitable certificate found" 的提示(虽不直接说 SNI,但结合抓包可定位)
强制 Nginx 透传并指定上游 SNI
Nginx 本身不自动从原始请求提取 Host 来设置上游 SNI;它默认使用 proxy_pass URL 中的域名 作为 SNI。如果 proxy_pass 写的是 IP(如 https://192.168.1.100:443),SNI 就会丢失。
必须改用域名,并配合 proxy_ssl_server_name on 显式启用 SNI 发送:
location /api/ {
proxy_pass https://cdn.example.com/; # 必须是域名,不能是 IP
proxy_ssl_server_name on; # 关键:开启向上游发送 SNI
proxy_ssl_verify off; # 测试阶段可关;生产建议配好证书链并开启 verify
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}注意:proxy_ssl_server_name on 是 1.7.0+ 版本才支持,低于此版本需升级。
处理多级 CDN 场景下的 Host 与 SNI 分离
有些多级架构中,你希望 SNI 发送给最外层 CDN(如 cdn-a.com),但 Host 头发给内层(如 origin.internal)——这时不能只靠 $host。
解决方案:用变量分离控制:
- 用 resolver 配合变量解析真实 CDN 域名(避免 DNS 缓存干扰)
- 显式设定 SNI 域名(通过
proxy_ssl_name)和 Host 头(通过proxy_set_header Host)
示例配置:
resolver 8.8.8.8 valid=30s;
<p>location /assets/ {</p><h1>SNI 发给 cdn-edge.example.com</h1><pre class="brush:php;toolbar:false;">proxy_ssl_name "cdn-edge.example.com";
proxy_ssl_server_name on;
# Host 头发给源站(内层 CDN 或源站)
proxy_set_header Host "origin.internal";
# 实际代理到解析后的 IP + 端口(保持 HTTPS)
proxy_pass https://cdn-edge.example.com/;}
补充:检查上游 CDN 的 SNI 要求
不是所有 CDN 都强制校验 SNI,但像 Cloudflare Enterprise、Akamai、阿里云全站加速等商用 CDN,在开启“HTTPS 回源”且勾选“严格 SNI 校验”时会拒绝无 SNI 或 SNI 不匹配的连接。
务必确认:
- 你的 CDN 控制台中回源设置是否启用了 “SNI 开启” 或 “强制 SNI 校验”
- 回源域名是否已正确 CNAME 解析并完成证书绑定(SNI 域名必须有对应有效证书)
- 若使用私有证书,Nginx 需通过
proxy_ssl_trusted_certificate指定可信 CA 证书链


















