Nginx使用proxy_pass里的坑

第一坑

我们有一个后台服务A,架构是这样:

1
管理员 ====> nginx ====> AWS的云服务器(服务A)

此时在nginx上做了一个proxy_pass的转发,如下:

1
2
3
4
5
6
7
8
	server {
listen 8081;
server_name xxx.com;

location / {
proxy_pass aws云服务器的IP地址:对应端口;
}
}

一直用的挺好,但是后来这个服务业务量上来了,就扩了一台机器,也部署了A服务,然后在前面加上了一个ELB做最小连接数分配,如图:

1
2
3
								AWS的云服务器1(服务A)
管理员 ====> nginx ===> ELB ===>
AWS的云服务器2(服务A)

此时修改了一下proxy_pass的配置:

1
2
3
4
5
6
7
8
	server {
listen 8081;
server_name xxx.com;

location / {
proxy_pass http://aws的ELB域名地址:对应端口;
}
}

当时是好使的,但是第二天就不好使了,nginx调用结果是502 Bad Gateway,当时是分别curl路径然后又抓包,一路十三招查下来也没什么头绪。后来发现原来proxy_pass后面如果接的是域名地址的话,那么Nginx会在每次启动和重载设置时,使用DNS将域名解析为IP地址缓存下来,并在之后一直使用这个IP!只有通过nginx -s reload这样的重启才会强制刷新IP,所以当时在nginx上抓包发现跳转的IP其实并不存在。由于在AWS中ELB的内网域名对应的IP并不是一直不变的,这才导致了上面的问题。

新更改的配置如下:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name xxx.xxx.net;

resolver DNS服务器地址 valid=30s; #resolver 是 DNS 服务器地址, valid 设定 DNS 刷新频率
set $service_lb aws的ELB域名地址;

location / {
proxy_pass http://$service_lb; #若有路径的必要,就加上$request_uri
}
}

需要特别注意的一点是set语句不能写到 location 里面,否则不会生效。

第二坑

proxy_pass这个配置对相对路径和绝对路径也有很有讲究,比如:

1
2
3
location /proxy/ {
proxy_pass http://10.0.0.1:8080/; #这里以/结尾
}

当访问 http://127.0.0.1/proxy/a/b/c.txt 时,nginx匹配到/proxy/路径,把请求转发给10.0.0.1:8080服务,实际请求代理服务器的路径为
http://10.0.0.1:8080/a/b/c.txt(不带location目录)。

如果是这么写的:

1
2
3
location /proxy/ {
proxy_pass http://10.0.0.1:8080; #这里没有以/结尾
}

当访问 http://127.0.0.1/proxy/a/b/c.txt 时,nginx匹配到/proxy/路径,把请求转发给10.0.0.1:8080服务,实际请求代理服务器的路径为
http://10.0.0.1:8080/proxy/a/b/c.txt(带location目录),此时nginx会把匹配的proxy也代理给代理服务器。

proxy_pass后面接多一级路径的情况跟上面一致。

补充一个$host 400错误的故障

400这个状态码是Bad Request,基本上就是请求header或者是cookie有问题,而请求header的问题要么是头过大要么是头没有。在HTTP/1.0里不支持Host请求头的,而在HTTP/1.1中,Host请求头部必须存在,否则会返回400 Bad Request。如果请求的URI不包含所请求服务的主机名,则必须为Host头字段指定一个空值。

nginx如果配置了$http_host变量做反向代理时,后端真实的服务器是需要知道请求的host头,而$http_host的值是’’,所以会触发400这个错误。这个情况就需要用$hosts来替代$http_host

也就是说:proxy_set_header Host $host这一行的作用是把原http请求的Header中的Host字段也放到转发的请求里。如果不加这一行的话,nginx转发的请求header里就不会有Host字段,而服务器是靠这个Host值来区分你请求的是哪个域名的资源的。

还有一种400的情况:如果是GET请求没问题,而POST请求返回400错误,那么可能是websocket配置成了全局单独配置websocket的地址即可。

参考资料

https://www.jibing57.com/2018/11/27/nginx-with-dynamic-upstreams-to-ELB/
http://www.syyong.com/net/Using-nginx-s-proxy_pass-function-to-do-domain-name-forwarding-causes-an-accident.html
https://blog.nswebfrog.com/2018/09/09/nginx-proxypass-dns/
https://www.jianshu.com/p/b113bd14f584
https://www.cnblogs.com/woshimrf/p/nginx-proxy-rewrite-url.html

感谢您请我喝咖啡~O(∩_∩)O,如果要联系请直接发我邮箱chenx1242@163.com,我会回复你的
-------------本文结束感谢您的阅读-------------