nginx $uri 是 url 解码

nginx $uri 是 url 解码

我在用nginx作为反向代理,我有 2 条规则,例如:

location ~ ^/indirect {
  rewrite ^/indirect(.*) /foobar$1;
}

location ~ ^/foobar {
  set $url http://example.com/something/index.php?var1=hello&access=$scheme://$host$uri;
  proxy_pass $url;
}

因此,如您所见,我将$uri变量作为参数传递给代理页面(该$uri变量是 nginx 变量,请参阅http core module请参阅文档。

问题是,如果我访问http://example.com/foobar/hello%20world$uri变量包含 /foobar/hello world(如您所见,%20已被替换为其 url 解码值,一个空格)。然后,nginx 在执行 proxy_pass 行之前返回 http 状态代码 400(错误请求)(那里的后端未联系)。

还有可用的变量$request_uri,它保存客户端发出的原始请求 URI,因此在这种情况下它将保存正确的值以及序列%20。但我不能使用它,因为如果客户端通过路径/indirect$request_uri将包含/indirect/...,而我希望access传递给后端的参数始终是/foobar/...

有多个indirect类似规则(这是针对 DAV/calDAV/cardDAV 服务器的,并且有多个客户端连接到多个路径,所以我需要这些indirect类似规则),因此在那里执行是不可行的proxy_pass,并且有客户端直接转到该/foobar路径。

那么有没有什么办法可以$uri不经过 url 解码就可以得到它呢?

可能不可接受的事情:

  • 发送双重编码的请求,因为请求可能来自我无法控制的客户端
  • 在每个间接规则和“直接”规则中多次指定最终 URL,因为这会导致维护问题。

答案1

使用nginx/1.2.1,我无法重现您的问题%20,一旦解码为空格,就会导致任何400 Bad Request在 nginx 中;也许是来自上游?

无论如何,使用通过提供的有限状态自动机实际上并不困难rewrite指令停止$uri包含解码的请求,但仍执行请求的各种转换。

https://stackoverflow.com/questions/28684300/nginx-pass-proxy-subdirectory-without-url-decoding/37584637#37584637

这个想法是,当你$uri就地改变时,它不会被重新解码。而且,如你所知,我们已经在 中有了未解码的$request_uri。剩下的就是简单地将一个设置为另一个,然后就结束了。

server {
    listen 2012;
    location /a {
        rewrite ^/a(.*) /f$1 last;
    }
    location /i {
        rewrite ^ $request_uri;
        rewrite ^/i(.*) /f$1 last;
        return 400; #if the second rewrite won't match
    }
    location /f {
        set $url http://127.0.0.1:2016/s?v=h&a=$scheme://$host$uri;
        proxy_pass $url;
    }
}

server {
    listen 2016;
    return 200 $request_uri\n;
}

是的,rewrite ^ $request_uri;上面的部分确实起到了作用:

% echo localhost:2012/{a,i,f}/h%20w | xargs -n1 curl
/s?v=h&a=http://localhost/f/h w
/s?v=h&a=http://localhost/f/h%20w
/s?v=h&a=http://localhost/f/h w
%

(如果您也不想对“直接”内容进行解码,那么最简单的方法可能就是将其也设为“间接”。)

答案2

我发现的唯一方法是使用HttpSetMiscModule像这样:

location ~
 ^/indirect {
  set_escape_uri $key $1;
  rewrite ^/indirect(.*) /foobar$key;
}

location ~ ^/foobar {
  set_escape_uri $key $uri;
  set $url http://example.com/something/index.php?var1=hello&access=$scheme://$host$key;
  proxy_pass $url;
}

如果有人知道更好的方法(无需使用外部模块编译 nginx,因为我没有 root 权限)请告诉我!

相关内容