Nginx与Apache 之间的 HTTP 400错误

魅影危机

有同事今天碰到的一个问题:用户访问公网的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

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据