nginx 4xx 错误的可能原因
2013-10-30 21:09:03 阿炯

本站赞助商链接,请多关照。

总结一些日常运维工作所碰到的关于Nginx的4xx错误,以为后面碰到情况作参考。

403 Forbidden 只是一个HTTP状态码,表示在请求一个资源文件时nginx不允许你查看。

可能的场景有下面几种
服务器上的权限问题。
这个是403 forbidden最常见的原因。

为了保证文件能正确执行,nginx既需要文件的读权限,同时又需要文件所有父目录的可执行权限。例如,当访问/usr/local/nginx/html/freeoa.jpg时,nginx既需要freeoa.jpg文件的可读权限,也需要/,/usr,/usr/local,/usr/local/nginx,/usr/local/nginx/html的可以执行权限。

解决办法:设置所有父目录为755权限,设置文件为644权限可以避免权限不正确。

目录索引设置错误(index指令配置)
网站根目录不包含index指令设置的文件。如像在index中不定义index.php ,nginx直接返回403 Forbidden而不会去检查index.php是否存在。

解决办法:添加首页文件到index指令,常见的是index.php,index.jsp,index.psp或者自定义首页文件。

网站禁止特定的用户来源访问相关内容,如网站屏蔽某个ip访问。可使用deny来实现。

访问禁止目录浏览的目录,像设置autoindex off后在来访问目录。

用户访问只能被内网访问的文件。即这些定义的目录仅在nginx内访问,外部不允许访问。

413 Request Entity Too Large

在上传文件或post内容比较大的文章时,会报出这个错误。打开nginx主配置文件nginx.conf,找到http{}段,一般在nginx/conf/nginx.conf这个位置,找到http{}段,修改或者添加:
client_max_body_size 1g;#1g为允许最大上传的大小
client_body_buffer_size 128k;

然后重启nginx,要是以php运行的话,这个大小client_max_body_size要和php.ini中的如下值的最大值差不多或稍大,这样就不会因为提交数据大小不一致出现错误。
post_max_size = 2M
upload_max_filesize = 2M

重置一下nginx服务基本上可以解决此问题。

400 错误的请求的可能原因

服务器中的错误记录日志中类似于这种:
180.168.126.244 - - [24/Aug/2015:12:24:20 +0800] "-" 400 0 "-" "-"
111.207.253.210 - - [24/Aug/2015:12:31:37 +0800] "-" 400 0 "-" "-"
116.231.107.14 - - [24/Aug/2015:13:03:32 +0800] "-" 400 0 "-" "-"

分析
经过分析nginx的log文件,发现都是在一次正常访问之后产生的数个400错误,每次有大概连续出现1-6个不等,而且也并不是每次客户访问都会产生400错误。再观察产生400错误的前一次访问是很正常的,200状态码,正常的文件,正常的来路,正常的User-Agent… 一切都很和谐,那400是怎么来的呢?

通过仔细观察发现,所有产生400错误的前一次访问的User-Agent都是palemoon浏览器留下的,也就是说400错误是由palemoon浏览器产生的。但是经过本地抓包发现,palemoon是没有向服务器发送异常请求或者数据包的。

在抓包分析中发现,palemoon在访问服务器时发起的连接不止一个,一般有5到6个不等,而如果请求的资源不需要那么多连接时,palemoon就会关闭未用的连接,这项技术叫做pre-connection“预先连接”。通常我们访问一个网站时,第一个获取的是一个html主文件,而里面链接了网页所需要的css、js、图片等其他媒体资源文件,而一般资源文件和html文件是在一个域下的,预先连接就是在获取html之前就建立很多的tcp连接,而不是等到获取到html文件之后再去连接服务器获取其他的文件, 因为连接服务器是需要消耗一些时间的,所以这项技术可以很大程度上加快网页的呈现速度。这个与多线程下载到是很相似。

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

测试
要验证上面的分析结果很简单,打开命令行cmd窗口,在里面输入telnet serverip 80,等待连接成功之后直接关掉cmd,这时去查看nginx的log文件中就多了一条400错误记录。

pre-connection也是有缺点的,如果站长做了优化,使用了Cookie-free技术,或者网页和静态资源使用不同的服务器,那么网页需要的css、js、img就和主html不在同一个域下,也可能不在同一个IP上,那么pre-connection不仅是鸡肋,而且会对主html服务器产生不必要的负担。

其它原因
网上很多人写过相关的文章,大多的人的原因是因为 header 的头部大小超了,引起响应 400 告诉是 bad request.但其实还有一种可能,就是象端口测试工具,只是检查端口是否是活的。像 LVS 之类什么的,也会引起这种问题,然后日志中会出现大量的 400 错误。对于这个问题可以在nginx.conf中,将client_header_buffer_size和large_client_header_buffers都调大,可缓解此问题。


该文章最后由 阿炯 于 2019-10-10 13:57:15 更新,目前是第 2 版。