应使用map指令基于可信代理ip和x-forwarded-proto头生成$real_scheme_secure变量,再通过proxy_set_header x-forwarded-proto $real_scheme_secure透传真实协议,确保后端正确识别客户端https状态。

在负载均衡代理(如云厂商 SLB、CDN、WAF 或前置 Nginx)后面部署 Nginx 时,客户端的真实 HTTPS 状态容易丢失。Nginx 默认只看自身连接协议($scheme),而它接收到的可能是上游代理转来的 HTTP 请求,导致后端误判为 HTTP,引发重定向死循环、Secure Cookie 失效、CSRF 校验失败等问题。关键不是“Nginx 是否启用了 SSL”,而是“最外层客户端是否走 HTTPS”。
用 map 指令定义可信的协议变量
不能直接依赖 $scheme,因为那只是当前 Nginx 到上游代理之间的协议。应在 http 块中全局定义一个防伪造的协议变量,优先信任来自已知代理 IP 的 X-Forwarded-Proto:
- 先声明可信代理 IP 列表(例如 CDN 或 LB 的出口 IP):
map $remote_addr $trusted_proxy {<br> 192.168.10.1 1;<br> 203.208.60.1 1;<br> default 0;<br>} - 再基于可信性生成最终协议:
map "$trusted_proxy:$http_x_forwarded_proto" $real_scheme_secure {<br> "1:https" "https";<br> "1:http" "http";<br> default $scheme;<br>}
在 proxy_pass 前透传标准化协议头
有了 $real_scheme_secure,就能安全地设置转发头。该行必须出现在 proxy_pass 之前,且不可被覆盖:
proxy_set_header X-Forwarded-Proto $real_scheme_secure;- 配套补全其他关键头:
proxy_set_header Host $host;<br>proxy_set_header X-Forwarded-Host $host;<br>proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br>proxy_set_header X-Forwarded-Port $server_port;
后端框架必须主动读取并信任该头
Nginx 设置了头,后端不认等于白设。不同框架启用方式不同,但目标一致:用 X-Forwarded-Proto 替代默认协议判断逻辑:
-
Spring Boot(2.6+):设
server.forward-headers-strategy=framework,并确保server.tomcat.protocol-header=X-Forwarded-Proto -
Django:启用
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https'),并配置SECURE_SSL_REDIRECT=True(如需强制跳转) -
Express / Node.js:需显式检查
req.headers['x-forwarded-proto'] === 'https',不能只看req.protocol
验证与日志排查
可通过 access_log 快速确认协议识别是否生效:
- 在
http块中定义日志格式:log_format debug '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_proto" "$scheme" "$real_scheme_secure"'; - 观察日志中三者是否一致:
$http_x_forwarded_proto(上游传入)、$scheme(Nginx 当前连接)、$real_scheme_secure(最终可信结果) - 若
$real_scheme_secure始终为http,检查上游是否真传了X-Forwarded-Proto,以及 IP 是否在$trusted_proxy列表中











