https重定向问题根源在于nginx构造跳转地址时误用不可信的$host或$http_host变量,导致host注入、协议错乱或循环跳转;应改用$server_name、固定域名或白名单校验,并配合default_server拦截非法host请求,同时通过x-forwarded-proto和proxy_redirect确保后端协议感知与location头重写。

HTTPS 重定向出问题,往往不是证书没配好,而是重定向逻辑本身被客户端输入带偏了。最典型的表现是跳转到 http://xxx:443、https://evil.com,或反复跳转、协议错乱。这些问题根源不在 TLS 层,而在 Nginx 如何构造跳转地址、如何传递协议信息、以及是否信任了不可控的请求头。
用 $host 或 $http_host 做跳转目标
这是最普遍也最危险的错误。比如写成:
return 301 https://$host$request_uri;return 301 https://$http_host$request_uri;
这两个变量都直接来自客户端 Host 请求头,可被任意篡改。攻击者发一个 Host: evil.com 的请求,Nginx 就会跳转到 https://evil.com/xxx,形成 Host 注入漏洞。即使没被攻击,用户手动改 Host 也可能导致跳转失败或跨域异常。
HTTP → HTTPS 跳转未覆盖 default_server
只在某个 server_name 下配置了 301,但没设 default_server,会导致非法 Host 请求进入默认块——而该块若没做拦截,可能返回 200、500 或更糟的透传行为。正确做法是:
- 所有监听 80 端口的 server 块,至少有一个带
default_server标识 - 该块内不使用
$host,直接 return 444(关闭连接)或 403 - 合法域名走白名单校验后再跳转,例如:
if ($host ~* ^(example.com|www.example.com)$) { return 301 https://$host$request_uri; }return 403;
反向代理场景下协议与端口信息丢失
后端应用(如 Spring Boot、Django)生成重定向时,常依赖原始协议判断。若 Nginx 没透传协议头,后端就会误以为是 HTTP 请求,返回 Location: http://...,甚至带上 :8080 这类内网端口。典型症状是访问 https://a.com/path 自动跳成 http://a.com:443/path/。
必须同时配置两处:
-
proxy_set_header X-Forwarded-Proto $scheme;—— 告诉后端当前是 https 还是 http -
proxy_redirect ~^https?://[^/]+(/.*)$ $scheme://$host$1;—— 主动重写后端返回的所有 Location 头,统一为当前协议+域名
注意:proxy_redirect 必须放在 proxy_pass 之后才生效;$scheme 动态取值比硬写 https 更健壮。
SSL 配置掩盖了真正的重定向问题
有些报错看似是证书问题,实则是重定向链路断裂。例如:
- 浏览器提示
ERR_SSL_PROTOCOL_ERROR,但 Nginx 日志里有大量 301 记录 → 很可能是跳转目标用了错误端口(如https://example.com:8443) - 页面空白或无限重定向 → 检查是否后端返回
http://Location,而 Nginx 又没配proxy_redirect,导致浏览器反复尝试 HTTP → HTTPS 循环 - 地址栏出现
:443显式端口 → 后端生成 URL 时把X-Forwarded-Proto当成了http,且没过滤掉端口信息
调试时优先看 Nginx access log 中的 upstream_addr 和 upstream_http_location 字段,再对比浏览器开发者工具 Network 标签里的实际重定向响应头。











