首先看一下400错误的定义:
400 Bad Request 是一种 HTTP 错误状态码。HTTP/1.1 对 400 Bad Request的定义主要是:
开发了一个springboot接口服务(版本:
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/