I am using nginx as reverse proxy and I have 2 rules like:
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;
}
So, as you can see I'm passing the $uri
variable as a parameter to the proxied page (the $uri
variable is an nginx one, see the http core module
documentation).
Problem is, if I visit http://example.com/foobar/hello%20world
, the $uri
variable contains /foobar/hello world
(as you see, the %20
has been substituted with its url-decoded value, a space). And then, nginx returns http status code 400 (bad request) before executing the proxy_pass line (the backend there is not contacted).
There is also available the variable $request_uri
, which holds the original request URI as issued by the client, so in this case it would hold the correct value, with the %20
sequence. But I cannot use this because if a client goes through the /indirect
path, $request_uri
would contain /indirect/...
while I want that the access
param passed to the backend be always /foobar/...
.
There are multiple indirect
-like rules (this is for a DAV/calDAV/cardDAV server, and there are multiple clients out there that connect to multiple paths, so I need these indirect
-like rules), so it is not feasible to do the proxy_pass
there, and there are clients that go directly to the /foobar
path.
So is there any way to get $uri
without url-decoding it?
possible things that aren't acceptable:
- sending the request double-encoded, as the request may come from a client I have no control of
- specifying the final url multiple times in eacy indirect rule and also in the "direct" one, as it causes maintenance problems.
With
nginx/1.2.1
, I wasn't able to reproduce your issue of%20
, once decoded into a space, of causing any400 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.And, yes, the
rewrite ^ $request_uri;
part above does do the trick:(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.)
The only way I found is to use the HttpSetMiscModule like so:
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!