解决Nginx 反代 Tomcat/Springboot 出现的ContextPath, RequestURI, getScheme, ServerPort不对的问题
问题示例
从/amp 反代到 /crm

请求地址:

打印信息(重复斜杠解决首页问题的, 略)
能看到contextPath是tomcat自己的/crm 而不是浏览器请求的/amp
这样就有可能导致模板引擎(JSP, Thymeleaf之类)请求头拼接错误而导致的404
也有可能导致后台进行Redirect的时候404
这样就有可能导致模板引擎(JSP, Thymeleaf之类)请求头拼接错误而导致的404
也有可能导致后台进行Redirect的时候404
解决方案
继承HttpServletRequestWrapper类, 重写getContextPath, getRequestURI, getRequestURL, getScheme, getServerPort后两个解决nginx反代https时候的问题
nginx 配置
location /amp { proxy_pass http://127.0.0.1:8081/crm/; proxy_set_header Host $host; proxy_set_header Port $server_port; # 配合java代码 proxy_set_header Request-URI $request_uri; # 配合java代码 proxy_set_header Context-Path /amp; # 配合java代码 proxy_cookie_path /crm /amp; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 配合java代码 }
HttpServletRequestWrapper
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * 对nginx转发后的contextPath进行匹配修复 * @author xudaz */ @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming") public class ParameterRequestWrapper extends HttpServletRequestWrapper { private final String oldContextPath; private final String oldRequestURI; ParameterRequestWrapper(HttpServletRequest request) { super(request); oldContextPath = request.getContextPath(); oldRequestURI = request.getRequestURI(); } @Override public String getContextPath() { String unknown = "unknown"; //先从nginx自定义配置获取 String contextPath = super.getContextPath(); String temp = getHeader("Context-Path"); if (temp != null && temp.length() > 0 && ! unknown.equalsIgnoreCase(temp)) { contextPath = temp; } return contextPath; } @Override public String getRequestURI() { // 把requestURI 的 oldContextPath 部分替换成 新的 contextPath String requestURI = super.getRequestURI(); if ( requestURI.startsWith(oldContextPath) ) { requestURI = getContextPath() + requestURI.substring(oldContextPath.length()); } return requestURI; } @Override public StringBuffer getRequestURL() { // 把 RequestURL 的 oldRequestURI 部分替换成 新的 oldRequestURI StringBuffer requestURL = super.getRequestURL(); String toString = requestURL.toString(); if ( toString.endsWith(oldRequestURI) ) { requestURL.replace( toString.indexOf(oldRequestURI), toString.indexOf(oldRequestURI) + oldRequestURI.length() , getRequestURI()); } return requestURL; } @Override public String getScheme() { String unknown = "unknown"; //先从nginx自定义配置获取 String scheme = super.getScheme(); String temp = getHeader("X-Forwarded-Proto"); if (temp != null && temp.length() > 0 && ! unknown.equalsIgnoreCase(temp)) { scheme = temp; } return scheme; } @Override public int getServerPort() { String unknown = "unknown"; //先从nginx自定义配置获取 int serverPort = super.getServerPort(); String temp = getHeader("Port"); if (temp != null && temp.length() > 0 && ! unknown.equalsIgnoreCase(temp)) { serverPort = Integer.parseInt(temp); } return serverPort; } }
还没完 这是新的request, 还得用上 写个过滤器 对request对象进行替换
import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 替换适配nginx反代的request对象的过滤器 * @author xudaz */ @Component public class ParamsFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ParameterRequestWrapper paramsRequest = new ParameterRequestWrapper( (HttpServletRequest) request); chain.doFilter(paramsRequest, response); } @Override public void init(FilterConfig arg0) { } @Override public void destroy() { } }
欧克 效果↓

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!