我使用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; }
所以,正如你所看到的,我将$urivariables作为parameter passing给代理页面( $urivariables是一个nginxvariables,请参阅http core module文档)。
问题是,如果我访问http://example.com/foobar/hello%20world $urivariables包含/foobar/hello world (正如你看到的, %20已经被它的url解码值,一个空格)。 然后,nginx在执行proxy_pass行(后端没有联系)之前返回http状态码400(坏请求)。
还有可用的$request_urivariables,它保存了客户端发出的原始请求URI,所以在这种情况下,它将保存正确的值,并使用%20序列。 但我不能使用这个,因为如果客户端通过/indirectpath, $request_uri将包含/indirect/...而我希望accessparameter passing给后端总是/foobar/...
有多个indirect类规则(这是一个DAV / calDAV / cardDAV服务器,有多个客户端连接到多个path,所以我需要这些indirect类规则),所以这是不可行的那里有proxy_pass ,有些客户直接进入/foobarpath。
那么有没有办法得到$uri没有url解码呢?
可能的事情是不可接受的:
我发现的唯一方法是像这样使用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权限),请让我知道!
使用nginx/1.2.1 ,我无法重现您的问题%20 ,一旦解码到一个空间,导致在Nginx内的任何400 Bad Request ; 也许这是从上游来的?
无论如何,使用通过rewrite指令提供的有限状态自动机来阻止$uri包含已解码的请求实际上并不困难,但仍然执行各种请求转换。
这个想法是,当你改变$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/hw /s?v=h&a=http://localhost/f/h%20w /s?v=h&a=http://localhost/f/hw %
(如果你想让“直接”的东西不被解码,那么把它变成“间接”也许是最简单的。)