nginx 400状态码排查

  1. 云栖社区>
  2. 博客>
  3. 正文

nginx 400状态码排查

余二五 2017-11-16 22:46:00 浏览941
展开阅读全文

   最近,发现主站nginx的log中有很多400的错误,每天有几千万条,故决定对产生400错误的原因进行排查。分析nginx log,发现这种无效的400请求,总是在一个正常访问之后产生,一般出现一个或者几个。log格式如下:

wKiom1O2bvjzlv3FAAvZ-yla5Cc011.jpg

   

下面对这种情况进行分析:

一,nginx 400状态码含义

A client MUST include a Host header field in all HTTP/1.1 request messages . If the requested URI does not include an Internet host name for the service being requested, then the Host header field MUST be given with an empty value. An HTTP/1.1 proxy MUST ensure that any request message it forwards does contain an appropriate Host header field that identifies the service being requested by the proxy. All Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message which lacks a Host header field.

上面是http1.1的rfc关于host部分的解释,从上面我们了解到如果一个http1.1的请求没有host域,那么server应该给client段发送400的状态码,表明这个请求server不能处理。而对于nginx server来说,也遵循这样的方式,说明client发送了一个无效的请求,nginx server无法处理,故返回400的状态码。


参见:http://www.w3.org/Protocols/rfc2616/rfc2616.html

二,重现400请求

1,我使用pc端的chrome和ucweb浏览器,对网站进行访问,发现nginx log中的确会产生400的log,在nginx server端通过netstat看了一下client和server段建立的tcp连接数,每一个请求都是在3个以上。而是用ie和firefox浏览器对网站进行访问,发现不能重现400的log,在nginx server端通过netstat看了一下client和server段建立的tcp连接数,每一个请求都是只有1个。

2,使用telnet登录nginx的80端口

[@zw-81-90 ~]# telnet 10.13.82.96 80
Trying 10.13.82.96...
Connected to 10.13.82.96 (10.13.82.96).
Escape character is '^]'.
Connection closed by foreign host.

使用telnet连接后,直接断开连接即可

三,问题分析

第一种情况:

通过分析nginx的log,发现400请求中chrome和ucweb的ua比较多,通过查看相关的文章发现,这种请求和浏览器的自身策略有关系。

通常我们访问一个网站时,第一个获取的是一个html主文件,而里面链接了网页所需要的css、js、图片等其他媒体资源文件,而一般资源文件和主html文件是在一个域下的,而像chrome这种浏览器就会使用pre-connection,即在获取html之前就与nginx server建立多个的tcp连接,而不是等到获取到html文件之后再去连接服务器获取这个域名下的其他文件,因为连接服务器是需要消耗一些时间的,所以这项技术可以很大程度上加快网页的呈现速度。当然,浏览器同一时间针对同一域名下的请求也是有一定数量限制。

如果网页html链接的资源比较少,或者客户端有缓存,不需要连接下载,那么Chrome浏览器发出的5-6个连接很可能只有1个是需要的,其他的都得关闭掉,这样就产生了一个问题:连接了服务器,而没有发送任何请求,当然也没有host头了。对于这种情况,nginx是当做400错误来处理的,但由于连接已经关闭,错误信息不会发送到客户端,这就产生了日志文件中记录的400错误。


第二种情况:

使用telnet与nginx server建立个tcp连接,但是没有发送任何数据,当然也没有host头,之后四次挥手断开了连接。这种情况,nginx也是当作400的错误来处理的。使用tcpdump抓包,wireshark分析过程的截图如下:

wKioL1O2bu7hh5itAAQlM54bIYw529.jpg

当然,我们发现使用nagios的check_tcp插件对nginx server端口做检测或者使用keepalived的tcp_check功能对后端nginx端口的存活做检测,这两种情况都会在nginx log中产生400的请求。原因也很简单,就是第二种情况的理由,一般tcp check的方式,就是建立tcp连接,然后再reset或者四次挥手断开连接。

四,解决办法

nginx官方提供了一种方法,使用虚拟主机来匹配那些在nginx上没有匹配到host的请求。这样那些空host的请求,会被这个虚拟主机所匹配。如果不加这个虚拟主机的话,如果nginx上的虚拟主机没有对应client请求的host,nginx默认是用nginx.conf中第一个虚拟主机的处理,故400的请求就会出现在第一个虚拟主机的log中。虚拟主机配置如下:

server {

  listen 80 default_server;

  server_name _;

  return 404;

  access_log off;

}

总之,目前分析来看这类400的无效请求属于正常现象,可以用虚拟主机来消除400的请求。











本文转自 leejia1989 51CTO博客,原文链接:http://blog.51cto.com/leejia/1434564,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
余二五
+ 关注