13

Original URL: /api/url%2Fencoded%2F/?with=queryParams

nginx:

location /api {
    client_max_body_size 2G;
    proxy_pass https://oursite;
}

With this configuration, I was able to preserve the URL encoding when passing through the proxy. If I add a "/" after "oursite", it will decode the URL.

Problem:

Now the URL after being proxied still contains "/api/". I need to remove "/api/" only while still preserving the URL encoded parts.

Sean Bollin
  • 870
  • 2
  • 10
  • 17

2 Answers2

25

Not a long time ago there was identical question without an answer. In my opinion, you should rething api to not have such weird URLs. Another way is to have api on subdomain. – Alexey Ten Mar 11 '15 at 22:58

stackoverflow.com/q/28684300/1016033 – Alexey Ten Mar 11 '15 at 23:01

Year-old challenge accepted!

    location /api/ {
        rewrite ^ $request_uri;
        rewrite ^/api/(.*) $1 break;
        return 400;
        proxy_pass http://127.0.0.1:82/$uri;
    }

That's it, folks!

More details at Nginx pass_proxy subdirectory without url decoding, but it does work even with the query string, too:

%  curl "localhost:81/api/url%2Fencoded%2F/?with=queryParams"
/url%2Fencoded%2F/?with=queryParams
%
Community
  • 1
  • 1
cnst
  • 25,870
  • 6
  • 90
  • 122
  • For anyone working without nested paths, `location / { proxy_pass http://127.0.0.1$request_uri; }` works fine – diachedelic Oct 11 '17 at 10:33
  • @diachedelic, look at the last control group in [the more details answer](//stackoverflow.com/questions/28684300/nginx-pass-proxy-subdirectory-without-url-decoding/37584637#37584637) — the `$request_uri` in your example is redundant. – cnst Oct 12 '17 at 01:31
  • by gum you're right, my mistake was adding a slash like `proxy_pass http://127.0.0.1/;` which caused nginx to normalize the path (mentioned in docs here: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) – diachedelic Oct 13 '17 at 02:10
  • Same problem here on CentOS 6.4 and nginx 1.10.2. This answer fixed it. Now I'm wondering if latest version of nginx (1.13.9) still have this behaviour. This seems like a workaround not an Nginx classic configuration... – MaX Mar 08 '18 at 17:08
  • @MaX, there is no reason to update us that the behaviour still persists, as this is not a bug in nginx, will never be "fixed". BTW, if the solution is helpful, please don't forget to upvote. – cnst Mar 08 '18 at 18:31
  • I wasn't updating to say the behaviour still persist because I do not know know if it does. I'm just really surprised I could not find an option such as "decode_uri false". – MaX Mar 08 '18 at 20:18
  • 1
    @MaX, look at the linked trac issue from the linked [full "More details…" answer](//stackoverflow.com/questions/28684300/nginx-pass-proxy-subdirectory-without-url-decoding/37584637#37584637); the option you suggest would be a security risk, hence, it's nginx classic configuration to not provide it. There's very limited use case for this, and when it does strike, then the above workaround is 100% doable and is the best choice, really. (Thanks for upvote.) BTW, also, if you don't modify `$uri`, then no decoding is done. And you can't possibly have both without it being a total mess. – cnst Mar 08 '18 at 20:52
  • @cnst Thank you it is more clear now. You said " if you don't modify $uri, then no decoding is done", is it why this "problem" does not exist with location "/" and "proxy_pass http://127.0.0.1;"? – MaX Mar 08 '18 at 21:55
  • @MaX, exactly — as per usual, nginx just always does the right thing (even if at first sight it might not look like the right thing) – cnst Mar 08 '18 at 22:31
  • @cnst Ok thanks. Maybe I did a mistake but do you know why I cannot acces root (http://domain1.com/api/)? It result in Nginx 500 error and logs say "zero length...". I thought the return 400 would avoid that... – MaX Mar 08 '18 at 22:55
  • @cnst No idea? Maybe I misunderstood the return 400 part. – MaX Mar 13 '18 at 16:54
  • One thing here I still don't get it is how can decoding %2F once be a security feature, when you can still penetrate the reverse proxy server with double encoded "/" (%252F) – Norman Xu Dec 04 '18 at 10:27
0

Disclaimer: I am sure this looks like an hack - and maybe it is. It is using the auth-subrequest feature for something else than auth, but it works!

If you want to keep any url-encoded part after /api/ from the original $request_uri I use NJS to set a variable and use it afterwards in the proxy_pass

js_import /etc/nginx/conf.d/http.js; # Import your njs file here
js_set $encodedUrlPart 'empty'; # Define a variable

  location ~* api\/(.*)$ {
    auth_request /urlencode; #This will get executed before proxy_pass
    proxy_pass http://127.0.0.1:82/$encodedUrlPart;
}

and the http.js can look like this

function urlencode(r){
    let regex = "(?<=\/api\/)(.*$)";
    let url = r.variables.request_uri # this holds the original, non touched url
    let lastPart = url.match(regex);
    r.variables.encodedUrlPart = lastPart; 
    r.log("The encoded url part: " + r.variables.encodedUrlPart);
    r.return(200); // need to return 200 so the 'auth' doesn't fail
  }
  export default {urlencode};

Is this considered unsafe? We could do some checking in the njs part though!

hmrc87
  • 352
  • 1
  • 4
  • 12