唯一可靠做法是直接在nginx的location或server块中配置proxy_set_header x-forwarded-proto $scheme;并确保后端框架主动信任该头,否则将引发重定向死循环、secure cookie失效及csrf校验失败;$scheme动态取值http或https,必须置于proxy_pass前且不可被覆盖。

直接在 Nginx 的 location 或 server 块中写 proxy_set_header X-Forwarded-Proto $scheme;,并确保后端框架主动信任该头——这是唯一可靠的做法。
必须用 $scheme 动态传递,不能硬编码
硬编码 https 或 http 会导致严重问题:HTTP 访问时被误标为 HTTPS,触发重定向死循环;HTTPS 访问时又可能被标错,导致 Secure Cookie 失效、CSRF 校验失败。
-
✅ 正确:使用内置变量
$scheme,它会自动取值http或https,取决于客户端实际连接协议(由listen 80或listen 443 ssl决定) -
❌ 错误:写成
proxy_set_header X-Forwarded-Proto https;或proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;(后者若上游没传,变量为空) - 这一行必须放在
proxy_pass之前,且不能被其他同名指令覆盖
配套转发头要一起配齐
只传 X-Forwarded-Proto 不够,后端还原原始请求上下文还需要其他关键头:
-
proxy_set_header Host $host;:剥离端口,避免生成example.com:443这类错误跳转地址 -
proxy_set_header X-Forwarded-Host $host;:显式透传原始域名,防止后端拼错 Host -
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;:保留完整 IP 链,兼容多级代理 -
proxy_set_header X-Forwarded-Port $server_port;:尤其在非标 HTTPS 端口(如 8443)时关键
多层代理时建议用 map 做兜底
当请求经过 CDN(如 Cloudflare、阿里云全站加速)时,CDN 通常已设置 X-Forwarded-Proto。可优先复用,再 fallback 到 $scheme:
- 在
http块中定义:
map $http_x_forwarded_proto $real_scheme {
default $http_x_forwarded_proto;
"" $scheme;
} - 在
location中使用:proxy_set_header X-Forwarded-Proto $real_scheme; - 更安全的做法是结合可信代理 IP 判断,避免客户端伪造头(需配置
trusted-proxies)
后端必须主动信任并读取这个头
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'),并开启USE_X_FORWARDED_HOST = True -
Flask:用
ProxyFix中间件,例如app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1) -
WordPress:在
wp-config.php中加$_SERVER['HTTPS'] = 'on';或修改is_ssl()判断逻辑











