nginx $uri is url decoded
Solution 1:
With nginx/1.2.1
, I wasn't able to reproduce your issue of %20
, once decoded into a space, of causing any 400 Bad Request
within nginx; perhaps that's coming from upstream?
Regardless, it is actually not that difficult to use the finite-state automaton that is provided through the rewrite
directive to stop $uri
from containing the decoded request, yet still perform all sorts of transformations of the request.
https://stackoverflow.com/questions/28684300/nginx-pass-proxy-subdirectory-without-url-decoding/37584637#37584637
The idea is that when you change $uri
in place, it doesn't get re-decoded. And, as you know, we do already have the undecoded one in $request_uri
. What's left is to simply set one to the other, and call it a day.
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;
}
And, yes, the rewrite ^ $request_uri;
part above does do the trick:
% 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
%
(If you want the "direct" thing to not be decoded, either, then it'll probably be easiest to just make it an "indirect" as well.)
Solution 2:
The only way I found is to use the HttpSetMiscModule like so:
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;
}
If anyone knows of a better way (without having to compile nginx with external modules, because I don't have root access) please let me know!