场景
有个查询项目详情接口 /projects/{name}
,正好我们要查询名称为 root/name 的项目,此时我们不能直接访问 /projects/root/name
,这样会被系统误认为是要查询名称为 root 的项目。所以,在实际开发中需要将 root/name 先进行UrlEncode(编码)再拼接到url上,即 /projects/root%2fname
最近在访问Nginx代理的服务时,此类url的api调用一直是404的异常,然而不通过nginx代理,直接访问后端服务时,此类url的api调用又是正常的。
问题探究
通过搜罗网上的相关资料,发现是nginx自动进行 UrlDecode(解码)的问题。
下面对各种nginx配置进行实验,探究如何避免自动UrlDecode:
- 编写 nginx.conf
第一个server配置就是我们常规的代理配置,会将请求代理后端的服务上去,比如运行的springboot项目,tomcat、jetty等等服务。
配置文件中有多段location配置,我们逐一验证。验证某一端配置时,将其他配置时注释掉!
第二个server配置模拟被代理的服务(即上面说的tomcat、jetty等等)。这个server直接将请求的uri返回。所以可以通过返回信息得知被代理的服务接收到的url是否被decode。
events {
worker_connections 1024;
}
http {
server {
listen 8088;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:8099/;
}
#location / {
# proxy_pass http://127.0.0.1:8099;
#}
#location / {
# proxy_pass http://127.0.0.1:8099$1;
#}
#location / {
# if ($request_uri ~* ^/(.*)$) {
# proxy_pass http://127.0.0.1:8099/$1;
# }
#}
}
# 此server模拟被代理的服务。通过观察此服务接收到的请求url,确定url通过nginx时是否被decode
server {
listen 8099;
server_name localhost;
location / {
add_header request $request;
# $request_uri是Nginx内置的变量,表示请求的uri。
# 这里直接将uri返回。所以可以通过返回信息得知被代理的服务接收到的url是否被decode。
default_type text/html; return 200 $request_uri;
}
}
}
-
启动Nginx
.\nginx.exe
-
请求接口
/projects/root%2fname
,通过返回值判断nginx是否自动进行UrlDecode。(验证某一段配置时,需将其他配置时注释掉!)
- 开启第1段location配置,注释掉其他location,重载配置
.\nginx.exe -s reload
location / {
proxy_pass http://127.0.0.1:8099/;
}
此处使用 curl
命令发送请求,当然也可以使用postman等等,怎么方便怎么来。
$ curl -s localhost:8088/projects/root%2fname
/projects/root/name
结论:如上,返回结果是 /projects/root/name,即后端服务接收到的请求uri,说明这种形式的配置nginx会自动进行UrlDecode。
- 开启第2段location配置,注释掉其他location,重载配置
.\nginx.exe -s reload
location / {
proxy_pass http://127.0.0.1:8099;
}
$ curl -s localhost:8088/projects/root%2fname
/projects/root%2fname
结论:返回结果是 /projects/root%2fname,说明这种形式的配置nginx不会自动进行UrlDecode。(可行办法1)
- 开启第3段location配置,注释掉其他location,重载配置
.\nginx.exe -s reload
location / {
proxy_pass http://127.0.0.1:8099$1;
}
$ curl -s localhost:8088/projects/root%2fname
/projects/root%2fname
结论:返回结果是 /projects/root%2fname,说明这种形式的配置nginx不会自动进行UrlDecode。(可行办法2)
- 开启第4段location配置,注释掉其他location,重载配置
.\nginx.exe -s reload
location / {
if ($request_uri ~* ^/(.*)$) {
proxy_pass http://127.0.0.1:8099/$1;
}
}
$ curl -s localhost:8088/projects/root%2fname
/projects/root%2fname
结论:返回结果是 /projects/root%2fname,说明这种形式的配置nginx不会自动进行UrlDecode。(可行办法3)
总结
配置2、配置3、配置4均可避免Nginx 自动进行 url decode。
# 配置2
location / {
proxy_pass http://127.0.0.1:8099;
}
# 配置3
location / {
proxy_pass http://127.0.0.1:8099$1;
}
# 配置4
location / {
if ($request_uri ~* ^/(.*)$) {
proxy_pass http://127.0.0.1:8099/$1;
}
}
reference:
使用Nginx代理S3时,需要禁用URL解码。
如何避免Nginx将url decode
Nginx 官方文档 http://nginx.org/en/docs/
Nginx 内置变量 http://nginx.org/en/docs/varindex.html
https://gitlab.com/gitlab-org/gitlab/-/issues/18775#note_215017836
end