如何修复nginx在任何头部测试工具上抛出400错误的请求头?

53
我有一个使用nginx的网站,并使用标题测试工具进行测试,例如http://www.webconfs.com/http-header-check.php ,但每次都显示400错误请求。下面是来自该工具的输出。尽管我所有的页面在浏览器中加载得非常好,并且在Chrome控制台中查看时,它显示状态代码200OK。
HTTP/1.1 400 Bad Request => 
Server => nginx
Date => Fri, 07 Sep 2012 09:40:09 GMT
Content-Type => text/html
Content-Length => 166
Connection => close

我真的不明白我的服务器配置有什么问题?

经过一些搜索,建议使用以下方式增加缓冲区大小,我已经按照以下方式增加了:

large_client_header_buffers 4 16k;

结果仍然相同。

有人能指导我朝正确的方向吗?


92
当 nginx 返回 400(错误请求)时,它将在错误日志中记录原因,记录级别为“info”。因此,找出问题的明显方法是配置 error_log 记录“info”级别的日志消息,测试时查看错误日志即可。 - Maxim Dounin
抱歉回复晚了。是的,谢谢Maxim的帮助。你说得对,日志最终帮助了我,我一直在更改错误的ini文件(因为使用了反向代理,所以设置在多个服务器上)。我的错。我会在这里详细解释一下。 - ʞɹᴉʞ ǝʌɐp
问题解决了吗? - lulalala
4
@deepak,你是如何解决这个问题的?谢谢。 - chr1x2
9个回答

88

正如Maxim Dounin在上述评论中所说:

Nginx返回400(错误请求)时,它会将原因记录到错误日志中,记录级别为“info”。因此,查找问题的明显方法是将error_log配置为记录“info”级别的消息,并在测试时查看错误日志。


7
为了易于阅读,我在此将Maxim Dounin的评论复制/粘贴到这里。所有功劳都应归给他,如果这个答案有帮助,请为他点赞。 - ejoubaud
8
请确认在 nginx 中开启了调试日志记录。您可以通过在站点的配置文件中定义错误日志行后面添加 debug 来完成此操作。因此,您将拥有类似于 server { error_log /path/to/error.log debug; 的内容。 - skyjacks
11
@skyjacks 我按照你写的做了,但还是没有日志。 - holms
3
我遇到了与Holms和Nicolae相同的问题。 - Krym
5
在我的情况下,该请求发送时携带了无效的主机头值。 - Nitb
显示剩余4条评论

28

按照Emmanuel Joubaud的建议更改错误调试级别是有效的(编辑/etc/nginx/sites-enabled/default):

        error_log /var/log/nginx/error.log debug;

然后我重启nginx后,在使用uwsgi的Python应用程序中出现了错误日志:

        2017/02/08 22:32:24 [debug] 1322#1322: *1 connect to unix:///run/uwsgi/app/socket, fd:20 #2
        2017/02/08 22:32:24 [debug] 1322#1322: *1 connected
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http upstream connect: 0
        2017/02/08 22:32:24 [debug] 1322#1322: *1 posix_memalign: 0000560E1F25A2A0:128 @16
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http upstream send request
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http upstream send request body
        2017/02/08 22:32:24 [debug] 1322#1322: *1 chain writer buf fl:0 s:454
        2017/02/08 22:32:24 [debug] 1322#1322: *1 chain writer in: 0000560E1F2A0928
        2017/02/08 22:32:24 [debug] 1322#1322: *1 writev: 454 of 454
        2017/02/08 22:32:24 [debug] 1322#1322: *1 chain writer out: 0000000000000000
        2017/02/08 22:32:24 [debug] 1322#1322: *1 event timer add: 20: 60000:1486593204249
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http finalize request: -4, "/?" a:1, c:2
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http request count:2 blk:0
        2017/02/08 22:32:24 [debug] 1322#1322: *1 post event 0000560E1F2E5DE0
        2017/02/08 22:32:24 [debug] 1322#1322: *1 post event 0000560E1F2E5E40
        2017/02/08 22:32:24 [debug] 1322#1322: *1 delete posted event 0000560E1F2E5DE0
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http run request: "/?"
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http upstream check client, write event:1, "/"
        2017/02/08 22:32:24 [debug] 1322#1322: *1 http upstream recv(): -1 (11: Resource temporarily unavailable)

我查看了我的uwsgi日志,并发现:

        Invalid HTTP_HOST header: 'www.mysite.local'. You may need to add u'www.mysite.local' to ALLOWED_HOSTS.
        [pid: 10903|app: 0|req: 2/4] 192.168.221.2 () {38 vars in 450 bytes} [Wed Feb  8 22:32:24 2017] GET / => generated 54098 bytes in 55 msecs (HTTP/1.1 400) 4 headers in 135 bytes (1 switches on core 0)

www.mysite.local添加到settings.py的ALLOWED_HOSTS中解决了该问题 :)


        ALLOWED_HOSTS = ['www.mysite.local']

虽然我没有在Nginx后面运行uwsgi,而是在Tomcat上运行,但这确实有所帮助。我检查了Tomcat日志:org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level. java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986 - Ed Randall
我的问题与类似的设置有关,将 ALLOWED_HOSTS = ['.example.com'](注意前面的点)解决了。 - Alberto Chiusole

17

我曾经遇到相同的问题并尝试了各种方法。这个 400 错误是由上游代理引起的。但是调试日志中没有任何显示。

问题出在重复的 proxy_set_header Host $http_host 指令上,一开始我没有注意到它。 删除其中一个即可立即解决问题。 我希望在这种情况下,nginx 能够给出除 400 以外的其他错误提示,因为 nginx -t 没有发现任何问题。

P.S. 这是因为从旧版 nginx 1.10 迁移到新版 1.19 时发生的问题。显然之前是可以容忍的。


2
我可以重新确认,如果上游服务器中存在重复的“Host”头,则Nginx会返回400错误。尽管如此,它仍会将消息记录在错误日志中的“info”级别。此更改是在1.17.9版本中引入的,并符合RFC 7230:https://tools.ietf.org/html/rfc7230#section-5.4 - Alexander Azarov
感谢您的回答。顺便说一下,我以为我的错误日志是在信息级别上,但我注意到在我的nginx.conf文件中更高的位置有一个警告模式指令。似乎你不能在更深层次的树中覆盖它,以使其更详细,例如在服务器块中。 - Phil
你救了我的一天,我花了好几个小时来检查我的配置文件出了什么问题。谢谢你。 - Duc Trung Mai

9

为了澄清,在/etc/nginx/nginx.conf文件中,您可以在文件的开头放置以下行:

error_log /var/log/nginx/error.log debug;

然后重新启动nginx:

sudo service nginx restart

这样,您就可以详细说明nginx正在做什么以及为什么会返回状态码400。


显然,除非使用“--with-debug”选项编译了nginx,否则无法启用调试日志级别。如果遇到这种情况,最好使用“info”级别以确保安全。 - Phil

6

可能是URL请求中的编码无效,例如未经编码的%。


2
当nginx返回400(错误请求)时,它会将原因记录在错误日志中,并在测试时查看错误日志,级别为“信息”(info)。请注意保留HTML标记。

0
在我的情况下,问题是路由器没有打开443端口。

0
如果您得到了像这样的日志:
client xx.xx.xx.xx closed keepalive connection 

对于这个问题:{{link1:“Connection:upgrade”会导致400错误,从未到达应用程序代码。由常见的nginx配置触发。#17081}}
只需设置proxy_set_header Connection $http_connection

-1

通常情况下,Maxim Donnie的方法可以找到原因。但我遇到了一个400错误请求不会记录到err_log中的问题。在tcpdump的帮助下,我找到了原因。


那个400的原因是什么?我们有一个类似的情况,但找不到原因。 - Germán Lena
尝试使用tcpdump查找原因。我的问题是,当电信运营商读取http头时,请求被阻止了。 - Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接