云服务器配置ssh用Nginx代理总404?换个网关就好?罪魁祸首竟是这个Host头!

服务器云盘源码 前言:谁没被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折磨过,或者团队里有人总踩这类坑,欢迎转发给他们——有时候解决问题的关键,就藏在一个被忽略的配置里。

阿里云 网站服务器

您好:云优数据云计算 www.yunyoushuju.cn 2核2G6M最低19.9元/月 欢迎开机

发表评论

评论列表
未查询到任何数据!