关键是透传x-forwarded-proto并重写location头:nginx需设proxy_set_header x-forwarded-proto $scheme和proxy_redirect匹配替换,后端须启用对应代理头解析且信任nginx ip。

核心是让后端知道用户实际用的是 HTTPS,同时让 Nginx 主动修正它返回的错误重定向地址。否则后端生成 http:// 开头的 Location,浏览器跳过去又被 Nginx 强制 301 回 HTTPS,形成死循环。
透传真实协议标识(X-Forwarded-Proto)
这是最关键的一步。Nginx 必须把原始请求的协议告诉后端:
- 在
location块中、proxy_pass之前添加:proxy_set_header X-Forwarded-Proto $scheme; -
$scheme会自动取值为https或http,不建议硬编码成https,否则 HTTP 入口或中间有其他代理时会出错 - 后端框架需启用解析该 Header:Spring Boot 加
server.forward-headers-strategy=framework;Django 配SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https');Next.js 需在服务端逻辑里手动读取
确保 Host 头准确且安全
Host 头影响后端路由判断和重定向生成,错误设置可能引发二次代理循环:
- 推荐写法:
proxy_set_header Host $host;(传递用户原始 Host,不含端口) - 避免用
$http_host,它可能带:443等端口,部分后端拒绝处理 - 如果后端只认某个固定域名(如
api.example.com),可写死:proxy_set_header Host api.example.com; - 搭配
proxy_http_version 1.1;可减少因 HTTP/1.0 导致的 Host 丢失风险
主动重写后端返回的 Location 响应头
即使后端协议识别正确,仍可能返回 http://localhost:8080/xxx 这类内网地址,Nginx 默认不改写,必须干预:
- 基础替换(适用于已知后端地址):
proxy_redirect http://localhost:8080/ https://$host/; - 通用正则匹配(推荐):
proxy_redirect ~^https?://[^/]+(/.*)$ $scheme://$host$1;
这条规则能捕获任意http://或https://开头的绝对路径,并替换成当前 Nginx 接收请求的协议 + 域名 + 路径 - 注意顺序:
proxy_redirect必须放在proxy_pass之后才生效 - 若后端返回相对路径(如
Location: /login),默认行为已足够,无需额外配置
检查后端是否信任代理头(防忽略)
有些应用默认不信任 X-Forwarded-* 头,即使 Nginx 发了也视而不见:
- 确认后端配置了可信代理 IP,例如 Spring Boot 的
server.tomcat.remoteip.remote-ip-header=x-forwarded-for和server.tomcat.remoteip.proxies-header=x-forwarded-by,并填入 Nginx 的真实 IP 段 - Java 应用若使用 Tomcat,还需检查
RemoteIpValve是否启用 - Nginx 自身作为 TLS 终止点时,后端若也启用了 HTTPS,应设
proxy_ssl_verify off;,避免协议不一致导致握手失败











