魅影危机
有同事今天碰到的一个问题:用户访问公网的Nginx服务器,Nginx再把用户请求转发到内网的Tomcat服务器的80端口,现在内网Tomcat服务器的应用想拆分,不同应用用不同的Tomcat来运行,这样就在内网配置了一台Apache(不方便改Nginx的转发规则,那个Nginx是别人弄的),根据应用的url转发到对应的Tomcat。出现的问题是:从公网访问Nginx出现HTTP 400错误,从Apache访问日志可以看到,请求是过来了,但没转发给后端的Tomcat,Apache直接返回400;如果Nginx直接转发到Tomcat也是可以的,从内网通过Apache访问Tomcat也是可以的。
所以问题应该是Nginx到Apache的请求有问题。这个同事知道我以前配置过Nginx,所以找我要Nginx转发到Tomcat的配置。
在本地测试机上,Nginx的转发配置都没问题,从 Nginx --> Tomcat
或者 Nginx --> Apache --> Tomcat
都没问题。
没辙,我只好回自己工位去google。
HTTP 400
HTTP协议定义 400-499
错误码为客户端请求错误,400
对应的描述是 "Bad Request"
,非常简洁啊,只知道是请求有问题,问题在哪里不知道,帮助不大啊。
新的希望
上厕所回来的时候突然想起以前我测试XMLHTTPRequest的跨越问题的情况:哪怕一个域名和一个IP指向的是同一台主机,浏览器也认为是跨越的,为了产生跨域的效果,请求跨域的资源的时候,是通过IP访问的;由于我的Nginx的配置文件的ServerName只指定了域名coderbee,没有指定主机IP,所以返回404了,所以我又在Nginx的配置文件里指定ServerName包括IP,访问就ok了,跨域效果有了。(广告:XMLHTTPRequest的跨越处理可见博客 《html5 文件拖拽上传及跨域处理》http://coderbee.net/index.php/web/20130703/266)
上面的问题给我的想法:是不是Apache的ServerName配置错了,导致它不处理请求呢!!
问题的反击
本地测试下,发现指定的ServerName跟请求头里Host不同时还真不处理,马上转到测试服务器上去测试,发现还是不行,不管ServerName配置成什么,指定了多少个,甚至不指定,都还是不行!!
这到底是要弄哪样啊!!
绝地归来
没办法了,既然看起来是跟请求头里Host头有关,就看看Nginx转发配置是怎么设值的。
不看不知道,一看吓一跳:竟然设置了两次Host头:
proxy_set_header Host $host; proxy_set_header Host $http_host;
果断注释掉再测试,KO了!
突然有种淡淡的忧伤:你妹的,不懂就用默认配置、最小配置啊!
不是总结的总结
从这里可以看出,不同的web服务器对HTTP协议的要求是不同的,Nginx直接转发到Tomcat,Tomcat能处理,而转发到Apache,Apache认为是个错误请求。
百思不得其解的时候不妨上个厕所,估计有所帮助。
先流水到这里吧,再上个厕所,准备下班了。
补充
2013-08-12:事后回想,如果当时进行抓包分析,估计花费的时间会更短。Linux下抓包已有内置工具tcpdump,在网上找了下,有一条非常简单的命令就可以进行HTTP 抓包:
tcpdump -A host coderbee.net and port 80
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。