根本原因是Java应用(如Tomcat)未正确识别Nginx转发的HTTPS协议,因未配置X-Forwarded-Proto头传递及Connector信任机制,导致request.getScheme()返回http,重定向生成http://链接。

这个问题本质是后端 Java 应用(如 Tomcat)在生成 302 重定向响应时,错误地用了 http:// 协议,而非原始请求的 https://。根本原因不是 Nginx 配置漏了,而是 Java 应用没读取或不信任 Nginx 传来的协议标识头。
为什么重定向会变成 HTTP
Java 应用(尤其是 Spring、Struts2 等)默认通过 request.getScheme() 判断当前协议。这个方法返回的是容器(Tomcat)收到的“原始协议”,而 Tomcat 直连 Nginx 时看到的是 http(因为 Nginx 用 HTTP 转发给它)。如果没额外配置,它就认为整个链路是 HTTP,所有 response.sendRedirect() 或框架级重定向都会拼出 http:// 链接。
- 现象:浏览器地址栏从
https://example.com/login→http://example.com/dashboard - 关键线索:仅重定向出问题,普通页面渲染、AJAX 请求 URL 正常(说明静态资源和接口路径本身没写死 http)
- 验证方式:抓包看响应头
Location: http://...,或在代码中打印request.getScheme()和request.getHeader("X-Forwarded-Proto")
Nginx 必须传递 X-Forwarded-Proto
只配 proxy_pass 不够,Nginx 必须显式把原始协议告诉后端。否则 Tomcat 完全不知道外面是 HTTPS。
- 在
location块里加这一行:proxy_set_header X-Forwarded-Proto $scheme; - 同时补全其他基础转发头(缺一不可):
proxy_set_header Host $host;、proxy_set_header X-Real-IP $remote_addr;、proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - 别用
proxy_redirect off—— 它会禁用 Nginx 对重定向响应头的自动改写,导致Location头里的协议/域名无法被修正 - 如果后端有多个域名或端口,加上
proxy_set_header X-Forwarded-Host $host;和proxy_set_header X-Forwarded-Port $server_port;
Tomcat 必须信任并解析这些头
光传头没用,Tomcat 默认忽略所有 X-Forwarded-*。必须在 server.xml 的 Connector 中启用远程地址解析,并指定哪些头可信。
立即学习“Java免费学习笔记(深入)”;
- 修改
Connector标签,加上:protocol="org.apache.coyote.http11.Http11NioProtocol"(确保用标准 HTTP connector,非 AJP) - 添加属性:
remoteIpHeader="X-Forwarded-For"、protocolHeader="X-Forwarded-Proto"、secure="true"(表示允许将X-Forwarded-Proto: https视为安全连接) - 完整示例:
<connector port="8080" protocol="HTTP/1.1" remoteipheader="X-Forwarded-For" protocolheader="X-Forwarded-Proto" secure="true" ...></connector> - 注意:不要只改
Engine下的Valve,那是旧版方案,优先用Connector级配置
Java 应用层还要检查是否覆盖了 scheme
有些老项目手动调用 request.getRequestURL() 拼链接,或框架配置了强制 HTTP。即使 Tomcat 解析正确,应用层仍可能绕过。
- Spring Boot 用户:确认
server.forward-headers-strategy=framework(推荐)或native,不能是none - Spring MVC 用户:检查是否有自定义
UrlBasedViewResolver或重写了response.encodeRedirectURL() - Struts2 用户:确认
struts.action.extension没强制写死协议;排查拦截器里是否调用response.sendRedirect("http://...") - 通用验证:在 Controller 里打印
request.getScheme(),如果仍是http,说明 Tomcat 配置未生效;如果是https但重定向还是错,问题就在应用代码
最常被忽略的一点:Nginx 和 Tomcat 的配置必须成对生效。只改 Nginx 头不改 Tomcat,或只改 Tomcat connector 不传 X-Forwarded-Proto,都会失败。中间任何一层(比如负载均衡器、CDN)若也参与转发,同样要透传并信任这些头。


















