skip to Main Content

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!

3

Answers


  1. 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;
        }
    
    Login or Signup to reply.
  2. 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
    
    Login or Signup to reply.
  3. 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 🙂

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search