4

Nginx proxy_pass with variables are hard to understand. Can some one explain how would i acheive the below scenario.

#first call /?proxytohost=http://blahblah.com
#second redirection to /
#third call /home 

location / {
    if ($arg_proxytohost) {
        set $proxytohost $arg_proxytohost;
        rewrite ^.*$ / break;
    }

    proxy_pass https://$proxytohost;   #first call it may recognize, third call definitely it cant

    proxy_intercept_errors on;
    error_page 301 302 307 = @handle_redirects;
}

location @handle_redirects {
    set $saved_redirect_location '$upstream_http_location';
    proxy_set_header  Host $proxy_host;
    proxy_pass $proxytohost$saved_redirect_location;   #this has proxytohost, i dont think it can recognize the variable here
}

EDIT:

Wanted to reverse proxy few instances which are dynamic (a.blahblah.com, b.blahblah.com, etc.). Each of those service instances has multiple redirects where i need to store cookies and redirect them (thats why i have @handle_redirects section).

If I set the variable $proxytohost above the location declaration as a.blahblah.com, then its working as expected.

But if i want to send that $proxytohost as a dynamic argument to first request and then set it to a variable and then proxy_pass it, it doesnt work.

For example, if assume my nginx is running in localhost:8080, this is what my expectation is

If i curl below

http://localhost:8080/?proxytohost=a.blahblah.com?authToken=aksdfkj

it should take me to a home page after using that token for authentication and redirecting to the home page,

http://localhost:8080/home

in this case /home is a content thats served from https://a.blahblah.com/home.

raksja
  • 191
  • 1
  • 2
  • 8
  • [If is Evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/) explains why this doesn't work. But I can't quite figure out what you are trying to do. Please explain it in English. – Michael Hampton Jan 24 '18 at 19:31
  • @MichaelHampton thanks for your response. I tried to explain a bit more, let me know if you need some more clarity around, i can rephrase. I tried to not use if, but i dont know how to acheive this without an if. – raksja Jan 24 '18 at 21:30

3 Answers3

3

That will not work.

If I understand correctly you want to proxy to the same host on successive request. Nginx handles request independent of each other. So on the 2nd and 3rd request if $arg_proxytohost is absent and the default you provided is used.

I suggest using Cookies for this. A little example below.

This needs to be in the http{} could be in conf.d in a separate file

map $cookie_p_host $p_host {
    default $cookie_p_host;
    ""      $host;
}

next we have the locations that go in to the server{}

location ~ /proxy/sethost/(?<p_host>.*) { 
    add_header Set-Cookie 'p_host=$p_host;path=/proxy'; 
    add_header Content-type text/html; 
    return 200 'cookie was "$cookie_p_host"<br>now set to "$p_host"'; 
}

location   ~ /proxy(?<p_uri>.*) { 
    resolver         8.8.8.8 ; 
    resolver_timeout 5s; 
    proxy_set_header Host $p_host; 
    proxy_pass       http://$p_host$p_uri; 
}

usage / explanation:

mapping

The mapping is used to avoid nasty if statements. If the cookie is set it is used as the hostname ( correct or not we don't check or care ). If it is not set (null/empty) we use the $host variable. Can change that to anything you want there.

first location

(http://localhost:8080/proxy/sethost/example.com) will set a cookie with the host set to example.com. Anything after /proxy/sethost/ will count as the host name!

second location

(http://localhost:8080/proxy) will now proxy to example.com. Added a resolver for dynamic lookups if not already somewhere else in the config. We need to set the Host header of the proxy request to the one requested and off we go.

Hope this helps. The redirect voodoo is another topic

Tero Kilkanen
  • 36,796
  • 3
  • 41
  • 63
evilBunny
  • 54
  • 4
  • This example doesn't work if you have multiple upstream servers. How do you handle the multiple upstream server case? – A X Jul 13 '22 at 06:12
2

Using a variable in the URL for proxy_pass without a resolver is only available on nginx-plus (The commercial version)

Adding a resolver means you'll be responsible for pointing it to a DNS that understands your URL.

I tried for ages to get this to work on the free version:

proxy_pass http://$myvariable:8081;

It's nginx's way of saying "pay me".

user507001
  • 21
  • 1
0

You can change the if directive to outside of the location block and it will have same meaning.

if ($arg_proxytohost) {
    set $proxytohost $arg_proxytohost;
    rewrite ^.*$ / break;
}
location / {
proxy_pass https://$proxytohost;   #first call it may recognize, third call definitely it cant

proxy_intercept_errors on;
error_page 301 302 307 = @handle_redirects;

}

Ilham Sulaksono
  • 593
  • 1
  • 10
  • 19