核心是透传并正确处理x-forwarded-proto等头以避免https跳转死循环:优先继承上游值,map兜底;同步可信host、port、ip头;后端需主动信任并解析这些头;nginx还需重写错误location响应头。

核心是让后端应用准确识别用户最初使用的协议(HTTPS),而不是只看到代理链最后一跳的 HTTP 连接。死循环本质是:用户走 HTTPS → 多层代理解密后以 HTTP 转发 → 后端误判为不安全 → 强制 301 跳回 HTTPS → 请求再次进入代理链,反复触发。
透传 X-Forwarded-Proto 并做智能兜底
不能简单写 proxy_set_header X-Forwarded-Proto $scheme,因为多层代理中,上层(如 Cloudflare、APISIX 或 CDN)可能已设好该头,下层 Nginx 若直接覆盖,反而会丢失原始值。
- 优先读取上游已传的值:
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; - 但需防上游没传——用
map指令兜底:
default $http_x_forwarded_proto;
"" $scheme;
}
再写:proxy_set_header X-Forwarded-Proto $real_scheme;
同时开启 underscores_in_headers on;,避免某些代理(如旧版 Cloudflare)用下划线传头时被 Nginx 丢弃。
确保 Host 和其他关键头同步可信
仅传协议不够。后端生成跳转 URL 时,若 Host 错、端口错、路径错,仍会拼出 http://127.0.0.1:8000 或 https://example.com:8443 这类错误地址,引发二次跳转或失败重试。
-
proxy_set_header Host $host;—— 不要用$http_host,它可能带端口,也不要用硬编码域名 -
proxy_set_header X-Forwarded-Host $host;和proxy_set_header X-Forwarded-Port $server_port; -
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
后端必须主动信任并解析这些头
Nginx 传了,后端不认等于白传。不同框架启用方式不同,但目标一致:用 X-Forwarded-Proto 替换默认的协议判断逻辑。
- WordPress:在
wp-config.php开头加if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') $_SERVER['HTTPS'] = 'on'; - Django:设置
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')且USE_X_FORWARDED_HOST = True - Spring Boot:配置
server.forward-headers-strategy=framework - Next.js / Express:需在请求处理中手动读取
req.headers['x-forwarded-proto']判断
拦截并重写后端返回的错误 Location 头
即使后端逻辑正确,仍可能返回绝对跳转地址(如 Location: http://localhost:3000/login),Nginx 默认不改写这类响应头,浏览器会直连内部地址,失败后可能触发额外跳转。
- 用正则精准重写所有非当前域的跳转:
proxy_redirect ~^https?://[^/]+(/.*)$ $scheme://$host$1; - 确保
proxy_redirect写在proxy_pass之后,否则不生效 - 若后端固定返回某内网地址,可单独匹配:
proxy_redirect http://192.168.1.100:8080/ https://$host/;











