最近开发同事反馈在服务器内读取到的url在一个特定的地方多了一个/,本来应该是http://example.domain.url/servername/appname/dosomething
这样的地址,在服务器上收到的地址为http://example.domain.url/servername//appname/dosomething
,影响了后续的应用处理。
在应用服务器的前一层恰好是NGINX反向代理。网络硬件截取域名,将http://servername/appname/dosomething
丢给NGINX进行分发,NGINX通过配置upstream负载均衡,再通过proxy_pass拼接新的url,通过proxy_set_header重定义请求头。而在做转发的时候正是将servername替换为实际的服务器IP:PORT,这个操作看起来是影响服务的关键节点。
# upstream part
upstream servername {
server 192.168.100.1:8888;
server 192.168.100.2:8888;
check interval=5000 rise=1 fall=3 timeout=4000;
}
..........
# proxy_pass part
location /servername/ {
proxy_pass http://servername/appname/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Request-Context http://$host:$server_port/servername/;
proxy_set_header X-Real-IP $remote_addr;
}
location后带/不带/,proxy_pass后带/不带/,查资料发现不同位置会影响url转义结果,那么会不会遵循完整替换原则呢?
location /pass/ {
proxy_pass http://localhost:8080;
}
# http://localhost:8080/pass/xxx
}
location /pass/ {
proxy_pass http://localhost:8080/;
}
# http://localhost:8080/xxx
}
location /pass {
proxy_pass http://localhost:8080;
}
# http://localhost:8080/xxx
}
location /pass {
proxy_pass http://localhost:8080/;
}
# http://localhost:8080//xxx
}
这四种情况看起来是有三种结果,不过为了验证可以再进阶一下:
location /pass/ {
proxy_pass http://localhost:8080/app;
}
# http://localhost:8080/appxxx
}
location /pass/ {
proxy_pass http://localhost:8080/app/;
}
# http://localhost:8080/app/xxx
}
location /pass {
proxy_pass http://localhost:8080/app;
}
# http://localhost:8080/app/xxx
}
location /pass {
proxy_pass http://localhost:8080/app/;
}
# http://localhost:8080/app//xxx
}
可以看到2 3 6 7的方式是正确的,location
部分结尾带/则proxy_pass
部分结尾带/,location
部分结尾不带/则proxy_pass
部分结尾不带/,完整的遵从同义替换。因此本身我们的配置文件在这边书写是没错的。
那么是不是应用本身的问题导致的呢?
假想会不会是应用bug或者配置的问题导致应用接收的url错误,其实nginx转发并没有问题。
为了判断该问题,只能抓包。
在服务器安装了wireshark,过滤掉非http包,发现携带的url确实是http://example.domain.url/servername//appname/dosomething
这样的,问题还是在nginx上面。
删除proxy_set_header里的/变成了
# upstream part
upstream servername {
server 192.168.100.1:8888;
server 192.168.100.1:8888;
check interval=5000 rise=1 fall=3 timeout=4000;
}
..........
# proxy_pass part
location /servername/ {
proxy_pass http://servername/appname/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Request-Context http://$host:$server_port/servername;
# 注意servername后面的/去掉了
proxy_set_header X-Real-IP $remote_addr;
}
再进行抓包,url变成http://example.domain.url/servername/appname/dosomething
正常了,看来罪魁祸首找到了。
proxy_set_header用来重新定义或添加字段传递给代理服务器的请求头。在没有定义proxy_set_header时会继承之前定义的值。也就是如果前道网络设备修改了请求头以后,nginx不作修改的话会原样转发请求头;但是也可以修改里面的文本和变量或者是它们的组合。
那么X-Forwarded-Request-Context
修改的是请求头的哪一部分就是现在要了解的重点了,这个参数确实过于冷门,翻遍全网只有在esri的问答网站有这样一个回答(回答地址)
X-Forwarded-Url-Base: web adaptor context
X-Forwarded-Request-Context: this appears to be the same as the web context url setting
webAdaptorID: web adaptor id
X-Forwarded-For: an external ip
这里就是设置web url的,并且目前看到只有arcgis的应用读取这个设置,看来确实会影响web url,但是为什么不遵循跟proxy_pass
一样的替换原则,这个只有该模块的开发者才能知道了。注意proxy_set_header
来自内置模块ngx_http_proxy_module
,不属于第三方模块。
那么不如来引申一下,深入了解一下proxy_set_header模块。这个后续会单独写一篇博客。
在查找这个问题的时候,我看到了另一个NGINX处理//的方式。地址
前端引用css,js时如果写的…/…/之类的引用方式,那么在访问链接中加入多个斜杠会导致引用失败.
可以通过nginx对多个斜杠进行处理
如访问 public//view//a//test.html 重定向到 public/view/a/test.html
merge_slashes off;
rewrite (.*)//(.*) $1/$2 permanent;