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.
  • 1
    This whole setup is its own maintenance problem. So I'm not sure why you would reject a solution because it might be a "maintenance problem". Nov 5, 2012 at 16:52
  • @MichaelHampton yes, you are right, but if it's possible I don't want to make it even less maintainable Nov 6, 2012 at 8:15
  • Finally I ended doing external 301 redirects from all the indirect uri's to the direct one, and only having the proxy_pass there. Should I answer my own question with this workaround? Nov 12, 2012 at 9:39

2 Answers 2


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.


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$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 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.)


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!


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.