35

When I browse to this URL: http://localhost:8080/foo/%5B-%5D server (nc -l 8080) receives it as-is:

GET /foo/%5B-%5D HTTP/1.1 

However when I proxy this application via nginx (1.1.19):

location /foo { proxy_pass http://localhost:8080/foo; } 

The same request routed through nginx port is forwarded with path decoded:

GET /foo/[-] HTTP/1.1 

Decoded square brackets in the GET path are causing the errors in the target server (HTTP Status 400 - Illegal character in path...) as they arrive un-escaped.

Is there a way to disable URL decoding or encode it back so that the target server gets the exact same path when routed through nginx? Some clever URL rewrite rule?

1

2 Answers 2

31

Quoting Valentin V. Bartenev (who should get the full credit for this answer):

A quote from documentation:

  • If proxy_pass is specified with URI, when passing a request to the server, part of a normalized request URI matching the location is replaced by a URI specified in the directive

  • If proxy_pass is specified without URI, a request URI is passed to the server in the same form as sent by a client when processing an original request

The correct configuration in your case would be:

location /foo { proxy_pass http://localhost:8080; } 
3
  • 14
    I had to change http://localhost:8080/ to http://localhost:8080 in case anyone has the same situation as I did. Commented Aug 22, 2013 at 20:23
  • 7
    Why does Nginx decode the URI before passing it to the backend server? Wouldn't it make more sense if it kept the URI untouched? Commented Jan 7, 2014 at 7:46
  • 1
    @platypus, it is kept untouched, until you explicitly start performing the substitutions Commented Apr 6, 2018 at 21:30
21

Note that URL decoding, commonly known as $uri "normalisation" within the documentation of nginx, happens before the backend IFF:

  • either any URI is specified within proxy_pass itself, even if just the trailing slash all by itself,

  • or, URI is changed during the processing, e.g., through rewrite.


Both conditions are explicitly documented at http://nginx.org/r/proxy_pass (emphasis mine):

  • If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive

  • If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed, or the full normalized request URI is passed when processing the changed URI


The solution is to either omit the URI as in OPs case, or, indeed, use a clever rewrite rule:

# map `/foo` to `/foo`: location /foo { proxy_pass http://localhost:8080; # no URI -- not even just a slash } # map `/foo` to `/bar`: location /foo { rewrite ^ $request_uri; # get original URI rewrite ^/foo(/.*) /bar$1 break; # drop /foo, put /bar return 400; # if the second rewrite won't match proxy_pass http://localhost:8080$uri; } 

You can see it live in a related Stack Overflow answer, including control group.

12
  • 2
    The documentation is confusing here. Both forms contain a URI. It is the path component that is present in one and missing in the other. Commented Apr 7, 2018 at 16:36
  • @MichaelHampton, I disagree — the PATH is generally called the URI, so, the one without the path, doesn't contain the URI. Commented Apr 7, 2018 at 18:39
  • 4
    Is it just me or is the standard behaviour wacky? We don't want URLs changed just because we happen to rewrite to a path instead of to the root!! Commented Jun 15, 2020 at 12:27
  • 1
    If I pass /foo%20bar to NGINX and it passes literally /yo/foo bar (an invalid URL containing a space) downstream which then fails then the behaviour is wrong/buggy. See trac.nginx.org/nginx/ticket/1930 Commented Jun 16, 2020 at 5:56
  • 1
    That's an arrogant statement. I have taken their feedback onboard. Perhaps you can explain why you think it makes sense to decode URL elements into $1? I certainly have illustrated cases where it is a problem and BREAKS HTTP and don't see anyone offering examples where this is a good idea. Why whould NGINX decode URIs into variables?? How can we re-encode them?? Commented Jun 18, 2020 at 5:52

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.