Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
399 views
in Technique[技术] by (71.8m points)

Nginx pass_proxy subdirectory without url decoding

I need to write an nginx location directive to proxy requests to subdirectory to another server preserving urlencoding and removing subdirectory prefix.

Here's an artificial example — request like this:

http://1.2.3.4/api/save/http%3A%2F%2Fexample.com

should pass as

http://abcd.com/save/http%3A%2F%2Fexample.com

I tried several different ways. Here're couple of them:

  1. From this SO question

     location /api/ {
         rewrite ^/api(/.*) $1 break;
         proxy_set_header   X-Real-IP        $remote_addr;
         proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
         proxy_set_header   Host             $host;
         proxy_pass http://abcd.com;
     }
    

But it decodes the string, so http://abcd.com gets /save/http://example.com

  1. From another SO question

     location /api/ {
         proxy_set_header   X-Real-IP        $remote_addr;
         proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
         proxy_set_header   Host             $host;
         proxy_pass http://abcd.com;
     }
    

But it keeps subdirectory, so http://abcd.com gets /api/save/http%3A%2F%2Fexample.com.

What's needed is somewhere in the middle. Thank you!

UPD: Here's a ticket in nginx bug tracker

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

But there is no easy way to fix this nginx behaviour. There are some bugs in nginx trac, you could add yours. trac.nginx.org/nginx/…. So, I think that the simplest way is to have subdomain. – Alexey Ten Feb 24 '15 at 14:49

https://trac.nginx.org/nginx/ticket/727

If you want nginx to do something custom, you can do so using ?proxy_pass with variables (and the $request_uri variable, which contains original unescaped request URI as sent by a client). In this case it will be your responsibility to do correct URI transformations. Note though that this can easily cause security issues and should be done with care.

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!


Here's for the full proof.

The config file for nginx/1.2.1:

server {
    listen 81;
    #first, the solution
    location /api/ {
        rewrite ^ $request_uri;
        rewrite ^/api/(.*) $1 break;
        return 400; #if the second rewrite won't match
        proxy_pass http://127.0.0.1:82/$uri;
    }
    #next, a few control groups
    location /dec/ {
        proxy_pass http://127.0.0.1:82/;
    }
    location /mec/ {
        rewrite ^/mec(/.*) $1 break;
        proxy_pass http://127.0.0.1:82;
    }
    location /nod/ {
        proxy_pass http://127.0.0.1:82;
    }
}

server {
    listen 82;
    return 200 $request_uri
;
}

Here are the results of running the queries for each location:

% echo localhost:81/{api,dec,mec,nod}/save/http%3A%2F%2Fexample.com | xargs -n1 curl
/save/http%3A%2F%2Fexample.com
/save/http:/example.com
/save/http:/example.com
/nod/save/http%3A%2F%2Fexample.com
%

Note that having that extra return 400; is quite important — otherwise, you risk having a security issue (file access through //api etc), as Maxim has briefly mentioned in your trac ticket.


P.S. If you think using the rewrite engine as a finite-state automaton is super cool, you might also want check out my http://mdoc.su/ project, or fork it github.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...