Nginx 通过upstream反向代理报 400 Bad Request

首先看一下400错误的定义:

400 Bad Request 是一种 HTTP 错误状态码。HTTP/1.1 对 400 Bad Request的定义主要是:

  • 语义有误,当前请求无法被服务器理解
  • 请求参数有误
  • 丢包导致异常

背景一:

开发了一个springboot接口服务(版本:<version>2.0.3.RELEASE</version>),使用nginx的upstream作为反向代理,default.conf配置如下:

upstream infer_worker {
    server 10.86.72.5:8080;
    server 10.86.72.15:8080;
    keepalive 1024;
}
server {
    listen              80 default;
    server_name         localhost;
    location = /infer {
        proxy_pass          http://infer_worker;
        proxy_set_header    Connection "";
        proxy_http_version  1.1;
    }
}

部署好后开始测试,从nginx 的access日志中发现http的status返回400。印象中以前的项目都是这么配置的,为什么会出错呢?google以后发现是由于springboot升级后导致内置tomcat的升级导致的。

https://github.com/spring-projects/spring-boot/issues/13236 从这里可以看出问题的原因是带有下划线的Host的http请求,tomcat认为是有问题。那为什么之前版本的tomcat是正常的呢? 新版本的tomcat对这个头部进行了校验,旧版本没有校验。好了,到这里我们就知道了,其实对于带有下划线的Host,tomcat是遵循的RFC1-1034的规范的,所以tomcat的处理是正确的。

说明:查看springboot中tomcat的包只需要查看maven依赖即可。

解决方法

1)设置proxy_set_header HOST $host

在location块中加上proxy_set_header HOST $host。(注意不能同时加上这两个:proxy_set_header HOST $host;proxy_set_header Host $http_host;)

分析:nginx在没有配置proxy_set_header HOST $host 的时候,在转发http请求的时候会默认把upstream的名称作为Host头部的内容。如果像上面例子中那样,upstream名称后面有下划线,就会被tomcat收到,从而报错。proxy_set_header HOST $host这个配置的主要是在nginx在转发htp请求的时候会加上实际的Host请求头。如http请求是 http://abc.com/hello,那么nginx在转发http请求的时候会原封不动的把host请求头(Host:abc.com)转发给后台服务。

2)upstream后面的名称去掉下划线:

从上面的分析中可知,去掉upstream后面名称中的下划线也可以避免这个问题。

参考:https://googlevip8.com/post/47

背景二:

upstream test_interface {
    server 192.168.12.2 8080;
}

server {
    listen       12345;
    server_name  localhost;
    location / {
        proxy_pass http://test-interface;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

现象:返回的http状态吗400.查看日志没有抱错。

原因:Host 为空。

解决方法:设置host值

location / {
        proxy_pass http://test-interface;
        proxy_set_header Host $host; 
        #或者 
        proxy_set_header Host $http_host; 
        proxy_http_version 1.1;
        proxy_set_header Connection "";
}

 搜索该问题时,了解到:如果upstream转发域名时也会遇到这个问题,描述如下:

upstream test-interface {
    server abc.test.cn;
}

server {
    listen       12345;
    server_name  localhost;
    location / {
        proxy_pass http://test-interface;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

 所出现的现象就是,从 192.168.1.10 或者 192.168.1.11 请求 192.168.1.11 的 12345 端口,都会返回 400。

最初给请求头加上了 Host ,比如 proxy_set_header Host $host; 或者 proxy_set_header Host $http_host; ,但还是报错,于是只好抓包进行查看,包括经过 Nginx 12345 端口转发的请求,和直接在 Nginx 服务器上调用外网接口的请求。

在比对了之后发现,能调用成功的请求头中, Host 就是接口的域名地址,而调用失败的请求头中,该字段都是 IP 或者其他什么东西

 解决方法一:设置静态Host

upstream test-interface {
    server abc.test.cn;
}

server {
    listen       12345;
    server_name  localhost;
    location / {
        proxy_pass http://test-interface;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host abc.test.cn;
    }
}

  解决方法二:不实用upstream,直接proxy_pass 域名

server {
    listen       12345;
    server_name  localhost;
    location / {
        proxy_pass http://abc.test.cn;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

参考: 

https://priesttomb.github.io/%E6%8A%80%E6%9C%AF/2020/05/05/nginx-400-error-about-host/

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值