# Proxy\_pass based on (possibly changed) $host

**URL:** <https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816>\
**Category:** NGINX\
**Created:** [January 28, 2009, 12:05pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816 "2009-01-28T12:05:53Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 12:05pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/1 "2009-01-28T12:05:53Z")

</div>

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](http://bar.foo.com)” {  
proxy-core.backends = ( “10.0.0.1:80” )  
}  
$HTTP[“host”] == “[baz.foo.com](http://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/](http://bar.foo.com/)  
might be updated such that its Host header changes from [bar.foo.com](http://bar.foo.com) to  
[baz.foo.com](http://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

---

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 12:13pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/2 "2009-01-28T12:13:31Z")

</div>

On Wed, Jan 28, 2009 at 02:52:24AM -0800, Dave Bailey wrote:

> }  
> $HTTP[“host”] == “[baz.foo.com](http://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/](http://bar.foo.com/)  
> might be updated such that its Host header changes from [bar.foo.com](http://bar.foo.com) to  
> [baz.foo.com](http://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;
    }
}

```

---

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 3:59pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/3 "2009-01-28T15:59:09Z")

</div>

Hello,

On Jan 28, 2009, at 12:02 , Igor S. wrote:

> > $SERVER[“socket”] == “:80” {  
> > server {  
> > server\_name [baz.foo.com](http://baz.foo.com);  
> > [bar.foo.com/](http://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?

---

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 5:27pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/4 "2009-01-28T17:27:59Z")

</div>

Hi Igor,

(response at bottom)

On Wed, Jan 28, 2009 at 7:01 AM, Igor S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > > ```
> > > location / {
> > > 
> > > ```
> > > 
> > > server {  
> > > listen 80;  
> > > server\_name [baz.foo.com](http://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](http://bar.foo.com);  
location / {  
proxy\_pass [http://10.0.0.1:80](http://10.0.0.1:80);  
proxy\_set\_header Host $http\_host;  
}  
}

server {  
listen 80;  
server\_name [baz.foo.com](http://baz.foo.com);  
location / {  
proxy\_pass [http://10.0.0.2:80](http://10.0.0.2:80);  
proxy\_set\_header Host $http\_host;  
}  
location /bar/path/one {  
proxy\_pass [http://10.0.0.1:80](http://10.0.0.1:80);  
proxy\_set\_header Host “[bar.foo.com](http://bar.foo.com)”;  
}  
…  
}

Suppose that I receive the request:

GET /bar/path/one/of/many/thousands HTTP/1.1  
Host: [baz.foo.com](http://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](http://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

---

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 5:43pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/5 "2009-01-28T17:43:54Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 4:15pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/6 "2009-01-28T16:15:02Z")

</div>

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;
 }
```

---

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 6:15pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/7 "2009-01-28T18:15:03Z")

</div>

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](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;

```

---

<div class="post-metadata">

**Author:** ![Dave\_Bailey](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/858c86/32.png) [@Dave\_Bailey](https://www.ruby-forum.com/u/Dave_Bailey)\
**Post date:** [January 28, 2009, 6:53pm UTC](https://www.ruby-forum.com/t/proxy-pass-based-on-possibly-changed-host/158816/8 "2009-01-28T18:53:51Z")

</div>

Hi Igor,

On Wed, Jan 28, 2009 at 9:00 AM, Igor S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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
