Forum: NGINX proxy_pass based on (possibly changed) $host

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ff751c81227187a737dc2e102374e2a9?d=identicon&s=25 Dave Bailey (Guest)
on 2009-01-28 12:05
(Received via mailing list)
Hi,

I am porting a lighttpd module that I wrote to nginx.  I am new to
nginx.  I would like to know if it is possible to choose the
proxy_pass backend from the Host header within a server.  In lighttpd,
this is possible by:

$SERVER["socket"] == ":80" {
  $HTTP["host"] == "bar.foo.com" {
    proxy-core.backends = ( "10.0.0.1:80" )
  }
  $HTTP["host"] == "baz.foo.com" {
    proxy-core.backends = ( "10.0.0.2:80" )
  }
}

The module that I am porting to nginx will sometimes update the Host
header such that a different proxy backend should be chosen.  For
example, a request from a particular IP address to http://bar.foo.com/
might be updated such that its Host header changes from bar.foo.com to
baz.foo.com, and thus it is served by the 10.0.0.2:80 backend.  Is
there a way to specify a relationship between $host and proxy_pass in
nginx.conf within the same server { ... }?

-dave
5640e332954fc0006aea97a155ce0afd?d=identicon&s=25 Igor Sysoev (Guest)
on 2009-01-28 12:13
(Received via mailing list)
On Wed, Jan 28, 2009 at 02:52:24AM -0800, Dave Bailey wrote:

>   }
>   $HTTP["host"] == "baz.foo.com" {
>     proxy-core.backends = ( "10.0.0.2:80" )
>   }
> }

However, the better:

    server {
        listen   80;
        server_name   bar.foo.com;

        location  / {
            proxy_pass  http://10.0.0.1:80;
        }
    }

    server {
        listen   80;
        server_name   baz.foo.com;

        location  / {
            proxy_pass  http://10.0.0.2:80;
        }
    }


> The module that I am porting to nginx will sometimes update the Host
> header such that a different proxy backend should be chosen.  For
> example, a request from a particular IP address to http://bar.foo.com/
> might be updated such that its Host header changes from bar.foo.com to
> baz.foo.com, and thus it is served by the 10.0.0.2:80 backend.  Is
> there a way to specify a relationship between $host and proxy_pass in
> nginx.conf within the same server { ... }?

    map $remote_addr  $back1 {
        default       10.0.0.1:80;
        192.168.1.1   10.0.0.2:80;
        192.168.1.5   10.0.0.2:80;
    }

    server {
        listen   80;
        server_name   bar.foo.com;

        location  / {
            proxy_pass  http://$back1$request_uri;
        }
    }

    map $remote_addr   $back2 {
        default        10.0.0.2:80;
        192.168.10.1   10.0.0.1:80;
        192.168.10.5   10.0.0.1:80;
    }

    server {
        listen   80;
        server_name   baz.foo.com;

        location  / {
            proxy_pass  http://$back2$request_uri;
        }
    }
E6595fe908f559e34b87c313ad1b7312?d=identicon&s=25 Johan Bergström (Guest)
on 2009-01-28 15:59
(Received via mailing list)
Hello,

On Jan 28, 2009, at 12:02 , Igor Sysoev wrote:

>> $SERVER["socket"] == ":80" {
>    server {
>        server_name   baz.foo.com;
>> bar.foo.com/
>    }
>    map $remote_addr   $back2 {
>            proxy_pass  http://$back2$request_uri;
>        }
>    }

This is very similar to how i have things set up; but this map is
rather static. I "solved" this by using memcached as "map", a simple
FastCGI application written in C doing the lookup which returns the
backend to use. My question is if this map thing somehow could be
expanded to something of a more dynamic nature?
5640e332954fc0006aea97a155ce0afd?d=identicon&s=25 Igor Sysoev (Guest)
on 2009-01-28 16:15
(Received via mailing list)
On Wed, Jan 28, 2009 at 03:49:23PM +0100, Johan Bergstr?m wrote:

> >>proxy_pass backend from the Host header within a server.  In
> >>}
> >   }
> >
> >   map $remote_addr  $back1 {
> >           proxy_pass  http://$back1$request_uri;
> >       listen   80;
> backend to use. My question is if this map thing somehow could be
> expanded to something of a more dynamic nature?

You may write a variable handler in C/ngx_http_perl_module for $back1
and $back2, but the problem is now variable handlers are blockeing ones.
There is no way to postpone proxy_pass processing while variable
resolution.

So, currently you may only use X-Accel-Redirect:

     location / {
         fastcgi_pass  ...;

         # it returns
         # ...
         # X-Accel-Redirect: /xar"
         # X-Proxy: http://10.0.0.1:80/some/uri?args
     }

     location = /xar {
         internal;

         # you have to store $upstream_http_x_proxy somewhere as it
         # will be discarded just proxy module will start to work
         set   $xproxy  $upstream_http_x_proxy;

         proxy_pass   $xproxy;
     }
Ff751c81227187a737dc2e102374e2a9?d=identicon&s=25 Dave Bailey (Guest)
on 2009-01-28 17:27
(Received via mailing list)
Hi Igor,

(response at bottom)

On Wed, Jan 28, 2009 at 7:01 AM, Igor Sysoev <is@rambler-co.ru> wrote:
>> >       location  / {
>> >   server {
>> >       listen   80;
>> >       server_name   baz.foo.com;
>> >
>> >       location  / {
>> >           proxy_pass  http://$back2$request_uri;
>> >       }
>> >   }

I see, so there is no restriction on the number of server { .. }
blocks that bind to a given port - so the nginx server { .. } is a
name-based virtual host, like with Apache.

> So, currently you may only use X-Accel-Redirect:
>     location = /xar {
>         internal;
>
>         # you have to store $upstream_http_x_proxy somewhere as it
>         # will be discarded just proxy module will start to work
>         set   $xproxy  $upstream_http_x_proxy;
>
>         proxy_pass   $xproxy;
>     }

Hmm, interesting.  Suppose, then, that my module stores a hash table
that relates the incoming {Host, uri} to the upstream {Host, uri} for
a large number of {Host, uri} keys.  In addition to this mapping, I
would have some "default" location-based mappings in nginx.conf, such
as:

server {
  listen 80;
  server_name bar.foo.com;
  location / {
    proxy_pass http://10.0.0.1:80;
    proxy_set_header Host $http_host;
  }
}

server {
  listen 80;
  server_name baz.foo.com;
  location / {
    proxy_pass http://10.0.0.2:80;
    proxy_set_header Host $http_host;
  }
  location /bar/path/one {
    proxy_pass http://10.0.0.1:80;
    proxy_set_header Host "bar.foo.com";
  }
  ...
}

Suppose that I receive the request:

GET /bar/path/one/of/many/thousands HTTP/1.1
Host: baz.foo.com

In my module, I would look this up in a hash table using the {Host,
uri} as the key.  I might find that the request should be handled
upstream by 10.0.0.1:80 (i.e. the upstream server that contains
bar.foo.com's content) at some completely different URI.  So my goal
is to rewrite the URI and possibly the Host header as a result of what
I find in the hash lookup, and have the proxy module connect to the
correct upstream backend as a result (and retrieve the rewritten URI).

In your config sample, you have:

set $xproxy $upstream_http_x_proxy;
proxy_pass $xproxy;

Within the module's C source code that I write, could I do this
through the ngx_ API, as well as something similar to update the
upstream URI?  There will be too many entries in the hash table for me
to represent it in nginx.conf.

-dave
5640e332954fc0006aea97a155ce0afd?d=identicon&s=25 Igor Sysoev (Guest)
on 2009-01-28 17:43
(Received via mailing list)
On Wed, Jan 28, 2009 at 08:13:05AM -0800, Dave Bailey wrote:

> >> >
> >> >       default        10.0.0.2:80;
> >> >       }
> >> >   }
>
> I see, so there is no restriction on the number of server { .. }
> blocks that bind to a given port - so the nginx server { .. } is a
> name-based virtual host, like with Apache.

[ 1st part of response. ]

Yes, and actually server {} is mix of IP-, name-, and port-based virtual
hosts:

    http {

        server {
            listen  192.168.10.1;
            listen  192.168.10.1:8000;

            server_name   one.example.com  www.one.example.com;

            ...
        }

        server {
            listen  192.168.10.1;
            listen  192.168.10.2:8000;
            listen  9000;

            server_name   two.example.com  www.two.example.com
                          three.example.com  *.three.example.com;

            ...
        }

        server {
            listen  9000  default;

            server_name   four.example.com  www.four.example.com;

            ...
        }

    }

The request is routed according

1) IP:port
2) exact server_name
3) *.name
4) name.*
5) regex server_name
5640e332954fc0006aea97a155ce0afd?d=identicon&s=25 Igor Sysoev (Guest)
on 2009-01-28 18:15
(Received via mailing list)
On Wed, Jan 28, 2009 at 08:13:05AM -0800, Dave Bailey wrote:

> > So, currently you may only use X-Accel-Redirect:
> >     location = /xar {
> that relates the incoming {Host, uri} to the upstream {Host, uri} for
>   }
>     proxy_pass http://10.0.0.1:80;
> In my module, I would look this up in a hash table using the {Host,
> proxy_pass $xproxy;
>
> Within the module's C source code that I write, could I do this
> through the ngx_ API, as well as something similar to update the
> upstream URI?  There will be too many entries in the hash table for me
> to represent it in nginx.conf.

You may create $xproxy_url and $xproxy_host variable handlers in C
module
and use them as

     proxy_pass        $xproxy_url;
     proxy_set_header  Host $xproxy_host;
Ff751c81227187a737dc2e102374e2a9?d=identicon&s=25 Dave Bailey (Guest)
on 2009-01-28 18:53
(Received via mailing list)
Hi Igor,

On Wed, Jan 28, 2009 at 9:00 AM, Igor Sysoev <is@rambler-co.ru> wrote:
> You may create $xproxy_url and $xproxy_host variable handlers in C module
> and use them as
>
>     proxy_pass        $xproxy_url;
>     proxy_set_header  Host $xproxy_host;

Thank you, that is perfect!

-dave
This topic is locked and can not be replied to.