原创

Nginx记一次HTTP 400 事故


0. 序

这几天忙着给我的小破博客架构升级,没想到搞了很久,真的就一个小问题啊!

1. 起因

这个博客是用SpringBoot搭的,其实后台很简单,就全用的SpringBoot那一套。但是版本是1.x的,我看着不太爽,于是想换成2.x的。顺便前端也优化下,响应时间太慢了,调整了一下Nginx的Gzip压缩功能。

2.经过

后台其实很顺利,无非就是pom的依赖升级了一下。SpringData JPA 有些过时方法和已经删除的方法换了一下。换完之后很奇怪。Nginx访问直接Http 400了。400这个响应码,我印象比较深的是SpringMVC 自动参数绑定那块,之前遇到过一次,参数传少了,或者是请求头有问题,顺着这个思路,我逐一排查请求的问题,然而没结果。后来发现直接请求Tomcat没有问题。那问题就出在Nginx上。但是我比较不解,Nginx的配置我很久都没动过,应该不会有问题。在查看了Nginx的error.log 和 access.log 无果后,在百度谷歌后终于发现了问题所在。

3.解决

Nginx 400 这类问题其实主要是以下三个方面的原因引起的
  • 1. 请求头过大:通常是由于Cookie中写入了较大的值所引起。在nginx.conf中,将client_header_buffer_sizelarge_client_header_buffers都调大。显然,我的问题不是这种情况,我的Cookie值少,并且不也大,不可能超过最大的buffer范围。
  • 2. Tomcat某些版本对URL的限制,常见如下的错。请求中用了无效的字符。查看RFC规范知,url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~四个特殊字符以及保留字符,而我们的请求中出现了|、{}大括号。需要修改 Tomcat catalina.properties中添加tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}。这个也排除了,我直接请求Tomcat没有问题,并且我用的SpringBoot的内置Tomcat部署,应该没有坑。
10-Oct-2018 14:43:12.531 INFO [http-nio-8080-exec-7] 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 method name. HTTP method names must be tokens
        at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:428)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
  • 3. 客户端的调用方式没有使用 Host 参数,Nginx传递了空的Host头给服务端,一旦Nginx设置了proxy_set_header Host $host,空Host头就传给了后端。然而,在http 1.1的规范中,Host只要出现空,就会返回400,所以出现了这个故障。而对于需要在Host字段里带上端口信息的,则仍需要配置proxy_set_header Host $host:$server_port.我的就是这个问题。在Nginx中注释掉下面这行代码就OK了。
#  proxy_set_header Host $host

4. 后续

Nginx还得好好学一下!!!太重要了
bug
nginx
  • 作者:管理员(联系作者)
  • 发表时间:2020-03-19 09:48
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 公众号转载:请在文末添加作者公众号二维码
  • 微信公众号

    评论

    留言