服务器云盘源码 前言:谁没被Nginx代理的404坑过? 前几天帮业务排查问题,差点没被一个Nginx代理的404搞破防。 场景很简单:前端用Nginx代理到Tomcat,访问nginx_url/web/···
服务器云盘源码
前言:谁没被Nginx代理的404坑过?
前几天帮业务排查问题,差点没被一个Nginx代理的404搞破防。
场景很简单:前端用Nginx代理到Tomcat,访问nginx_url/web/user/hi直接404;但跳过Nginx,直接访问tomcat_url/user/hi,接口稳稳返回"hello hi"。更诡异的是,把代理地址换成Spring Cloud Gateway,同样的路径nginx_url/web/user/hi居然秒通了!
同样的Nginx配置,换个后端就好使?这到底是Nginx抽风了,还是Tomcat和Gateway有仇?
如果你也遇到过「直连后端正常,代理就报错」「换个中间件突然好了」的情况,那这篇文章一定要看完——其实问题的根儿,就藏在一个容易被忽略的配置里:proxy_set_header Host。
诡异现象背后:Tomcat和Gateway,对Host的态度差太多
为啥同样的Nginx配置,代理Tomcat就404,走Gateway就正常?
关键差别,就在于后端服务怎么处理「Host请求头」。
1. Spring Cloud Gateway:我认路径,不认Host
Gateway的脾气很随和:它路由请求时,主要看「路径前缀」(比如/user-service/),顶多再看看请求头里的特殊标记(比如X-Request-Id)。哪怕你给它的Host头乱填,只要路径对了,它就能精准找到后端服务。
大白话就是:Gateway找服务,靠的是「路标」(路径),不是「门牌号」(Host)。
2. Tomcat:Host不对?门都没有!
Tomcat就死板多了:它天生认「Host头」当门牌号。收到请求时,会先扒拉Host头,根据这个值判断该把请求分给哪个虚拟主机。如果Host头和它预期的不一样,哪怕路径再对,它也会摆摆手说没这个地址(404)。
大白话就是:Tomcat找服务,必须先对「门牌号」(Host),不对就拒客。
回到开头的问题:
当Nginx代理Tomcat时,配置里用了proxy_set_header Host $http_host(也就是把客户端的Host头原封不动传给Tomcat)。这时候Tomcat拿到的Host是nginx_url,但它自己预期的Host可能是tomcat_url,门牌号对不上,自然404。换成Gateway后,Gateway不管Host是啥,只要路径里有/user,就知道该转发给user-service,所以能正常返回。搞懂这3个变量,Host配置再也不踩坑
既然问题出在proxy_set_header Host,那到底该填啥?Nginx里有3个容易混淆的变量:$http_host、$host、$proxy_host,分清它们,90%的代理问题都能解决。
1. $http_host:客户端发啥,它就传啥(带端口)
这个变量就是个传声筒,客户端请求头里的Host是啥,它就原样转发,包括端口号。比如客户端发example.com:8080,它就传example.com:8080。
但有个坑:如果客户端没发Host头,它就空了。
适用场景:必须严格保留客户端原始Host信息时(比如需要根据客户端Host做特殊逻辑)。
2. $host:自动修门牌号(去端口)
这个变量更智能:
优先用客户端发的Host头,但会自动去掉端口(比如example.com:8080变成example.com);如果客户端没发Host头,就用Nginx自己监听的服务器名(比如Nginx配置的server_name)。适用场景:大部分常规Web应用,需要一个干净的主机名时。
3. $proxy_host:后端服务器的真实地址(带端口)
这个变量只在反向代理时生效,值就是proxy_pass里填的后端地址(带端口)。比如proxy_pass http://tomcat:8080,那$proxy_host就是tomcat:8080。
它的核心作用:告诉后端我真实的地址是啥。
云服务器案例客户
适用场景:后端服务(比如Tomcat)需要知道自己的真实地址时。
一句话总结区别:
变量
本质
带不带端口
给后端的信息
粘土云服务器id
$http_host
客户端原始Host
可能带
客户端认为你是这个地址
$host
简化版主机名
不带
你的公开门牌号是这个
$proxy_host
后端真实地址
带
你的真实地址是这个
解决开头的问题:改一个配置就够了
知道了区别,再看开头的Nginx配置:
原来的配置用了proxy_set_header Host $http_host,相当于告诉Tomcat客户端认为你是nginx_url,但Tomcat只认自己的真实地址(比如tomcat:8080),所以404。
解决办法很简单:把Host改成$proxy_host,告诉Tomcat你的真实地址在这:
location/web/ {proxy_pass$WEB_TOMCAT/user/;proxy_set_headerX-Real-IP$remote_addr;关键修改:用$proxy_host传递后端真实地址proxy_set_headerHost$proxy_host;proxy_set_headerX-Forward-For$http_x_forwarded_for;其他配置...}改完之后,Tomcat收到的Host是$proxy_host(也就是$WEB_TOMCAT对应的真实地址),门牌号对上了,自然就返回hello hi了。
最后叮嘱:这3个原则记牢,少走90%的弯路
代理到Tomcat、Nginx等认Host的服务时,优先用$proxy_host(告诉它真实地址);代理到Gateway、API网关等认路径的服务时,用$host更稳妥(简化主机名);除非明确需要保留客户端原始Host(比如多租户按Host区分),否则别轻易用$http_host(容易带端口坑)。如果你也被Nginx代理的404折磨过,或者团队里有人总踩这类坑,欢迎转发给他们——有时候解决问题的关键,就藏在一个被忽略的配置里。
阿里云 网站服务器

发表评论
最近发表
标签列表