Zaak
Zaak

Reputation: 482

How to get nginx to do a redirect to url-encoded query parameter

I have a requirement to do a proxy call to url delivered via a query parameter as per example: My nginx proxy is deployed at: https://myproxy.net

if the redirect parameter is not url encoded I can do the call with this block:

  location /basepath {
        if ( $arg_redirect = '') { 
          return 400 "Missing redirect directive in request"; 
        }
        proxy_pass $arg_redirect;
        proxy_intercept_errors on;
        error_page 301 302 307 = @handle_redirects;
    }

the error intercepts and @handle_redirects then take care of othe 30X codes that might pop up at new destination.
This works for a request:
GET: https://myproxy.net/basepath?redirect=https://destination.com/somepath/uuid

What do I need to do to make it work for:
GET: https://myproxy.net/basepath?redirect=https%3A%2F%2Fdestination.com%2Fsomepath%2Fuuid

Additionally as part of spec it has to be pure nginx, not additional modules, lua etc. Thanks!

Upvotes: 3

Views: 10534

Answers (3)

Dmitry MiksIr
Dmitry MiksIr

Reputation: 4445

Ok, there is very weird and curious solution

server {
  listen 80;
  resolver x.x.x.x;
  location /basepath {
    if ($arg_redirect = '') {
      return 400 "Missing redirect directive in request";
    }
    proxy_pass http://127.0.0.1:80/basepath/$arg_redirect;
  }
  location ~ ^/basepath/(?<proto>\w+):/(?<redir>.+)$ {
    proxy_pass $proto://$redir;
  }
}

Nginx does not encode path with variables in proxy_pass and send it as is. So, I make $arg_* part of proxy_pass uri, send request to self and nginx will receive new request which will be decoded.

But because Nginx will clean path and replace // to / I split protocol part in regexp.

And ... I would never recommend using this solution, but it works :)

Upvotes: 3

anemyte
anemyte

Reputation: 20196

Actually, proxy_pass does normalisation by default, but it only affects $uri part. Thus you only need to decode the beginning of the passed string to get it working:

  location / {
    if ( $arg_redirect = '') {
      return 400 "Missing redirect directive in request";
    }
    if ( $arg_redirect ~ (.+)%3A%2F%2F(.+) ){ # fix :// between scheme and destination
      set $arg_redirect $1://$2;
    }
    if ( $arg_redirect ~ (.+?)%3A(.*) ){ # fix : between destination and port
      set $arg_redirect $1:$2;
    }
    if ( $arg_redirect ~ (.+?)%2F(.*) ){ # fix / after port, the rest will be decoded by proxy_pass
      set $arg_redirect $1/$2;
    }
    proxy_pass $arg_redirect;
  }

With the above I managed to access http://localhost/?redirect=http%3A%2F%2F127.0.0.1%3A81%2Fsfoo%20something%2Fs

The solution seems dirty and the only alternative using default modules is map (even less cleaner in my opinion). I'd rather split redirect argument into pieces: scheme (http or https), destination, port, and uri. With that you would be able to construct full address without rewriting:

proxy_pass $arg_scheme://$arg_dest:$arg_port/$arg_uri

Upvotes: 4

Tch
Tch

Reputation: 1055

try like this and let me know if it works

  location /basepath {
        if ( $arg_redirect = '') { 
          return 400 "Missing redirect directive in request"; 
        }

        set_unescape_uri $decodedredirect $arg_redirect;

        proxy_pass $decodedredirect;
        proxy_intercept_errors on;
        error_page 301 302 307 = @handle_redirects;
    }

Upvotes: 0

Related Questions