# Dynamic Redirects

**URL:** <https://www.ruby-forum.com/t/dynamic-redirects/186488>\
**Category:** NGINX\
**Created:** [April 7, 2010, 9:09pm UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488 "2010-04-07T21:09:49Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [April 7, 2010, 9:09pm UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/1 "2010-04-07T21:09:49Z")

</div>

NGINX Team,

Is there a way to capture the User-Agent from an HTTP Header, then query  
a  
database for that User-Agent, then based on the result of the database  
query, transparently route the request to an appropriate server?

Thanks,  
Sean

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [April 7, 2010, 9:32pm UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/2 "2010-04-07T21:32:04Z")

</div>

On Apr 7, 2010, at 12:09 PM, Harmer, Sean wrote:

> NGINX Team,
> 
> Is there a way to capture the User-Agent from an HTTP Header, then query a database for that User-Agent, then based on the result of the database query, transparently route the request to an appropriate server?
> 
> Thanks,  
> Sean

Sean-

Here is one way to do what you want, you will need to use redis as the  
database to lookup your backend with. You will need to compile nginx  
with two additional modules:

> **[GitHub - nginxinc/nginx-wiki: ARCHIVED -- Source for the now archived NGINX Wiki...](https://github.com/nginxinc/nginx-wiki)**
>
> ARCHIVED -- Source for the now archived NGINX Wiki section of https://www.nginx.com

[http://www.grid.net.ru/nginx/eval.en.html](http://www.grid.net.ru/nginx/eval.en.html)

Once you have both of those module compiled into your nginx you can  
use a config file like this:

worker\_processes 1;  
user root root;  
events {  
worker\_connections 1024;  
}

http {  
include mime.types;  
default\_type application/octet-stream;

sendfile on;

log\_format main '$remote\_addr - $remote\_user [$time\_local] ’  
'“$request” $status $body\_bytes\_sent “$http\_referer”  
’  
‘“$http\_user\_agent” “$http\_x\_forwarded\_for”’;

keepalive\_timeout 65;

server {  
listen 80;  
server\_name \_;

```
   access_log /var/log/nginx/redis.access.log main;

   error_log /var/log/nginx/redis.error.log notice;

   location / {
           eval_escalate on;
           eval $answer {
                   set $redis_key "$http_user_agent";
                   set $redis_db "0";
                   redis_pass 127.0.0.1:6379;
           }
           proxy_pass $answer;

   }

```

}  
}

Then in redis you will need to store key value pairs mapping the  
$http\_user\_agent to the server:port combo that should serve that user  
agent type:

redis.set “some-user-agent”, “[http://192.168.0.1:80](http://192.168.0.1:80)”

Then when a request comes into the nginx server and has a  
$http\_user\_agent value of “some-user-agent”, redis will return  
[http://192.168.0.1:80](http://192.168.0.1:80) and then nginx will proxy\_pass to the particular  
server.

Hope this helps point you in the right direction.

Cheers-

Ezra Z.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [April 7, 2010, 10:34pm UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/3 "2010-04-07T22:34:07Z")

</div>

Ezra - Thank you. That is very helpful.

It brings up another question: Once the match has been made in redis,  
is  
there a way to modify or append data to the User-Agent or HTTP HEADER?

Thanks again,  
Sean

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [April 7, 2010, 11:39pm UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/4 "2010-04-07T23:39:07Z")

</div>

Harmer, Sean ha scritto:

> NGINX Team,
> 
> Is there a way to capture the User-Agent from an HTTP Header, then query  
> a database for that User-Agent, then based on the result of the database  
> query, transparently route the request to an appropriate server?

The User-Agent is in $http\_user\_agent variable.

If you want to extract a more useful information, you need to use the  
perl module, or a custom module (but, as far as I know, there is no  
module that allow you to extract useful data from User-Agent header).

For database lookup, you can use the builtin map module:

> **[GitHub - nginxinc/nginx-wiki: ARCHIVED -- Source for the now archived NGINX Wiki...](https://github.com/nginxinc/nginx-wiki)**
>
> ARCHIVED -- Source for the now archived NGINX Wiki section of https://www.nginx.com

Manlio

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [April 8, 2010, 12:20am UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/5 "2010-04-08T00:20:05Z")

</div>

By transparent, I mean passing the http header through nginx to other  
servers. I’m just hoping nginx can help direct the request to the  
correct server.

Thx

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [April 7, 2010, 11:28pm UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/6 "2010-04-07T23:28:00Z")

</div>

Depends on what you mean by ‘transparently routing’ - is it choosing a  
different upstream (in case nginx is just a proxy) or redirecting to  
diferent host/server?

While you can prolly try use this  
[GitHub - openresty/drizzle-nginx-module: an nginx upstream module that talks to mysql and drizzle by libdrizzle · GitHub](http://github.com/chaoslawful/drizzle-nginx-module) (although iirc the  
possibility to get a single return value as variable back is still in  
TODO)

I would imagine that using some dynamic language (like php or even the  
embeded perl (if you really want to do this on server side)) is more  
simple  
and while client is being redirected its still more or less transparent  
to  
him.

With the nginx inbuilt features (without the DB backend) its like this -  
a  
simple rewrite:

if ($http\_user\_agent ~ MSIE) {  
rewrite ^(.\*)$ /msie/$1 break;  
}

> **[GitHub - nginxinc/nginx-wiki: ARCHIVED -- Source for the now archived NGINX Wiki...](https://github.com/nginxinc/nginx-wiki)**
>
> ARCHIVED -- Source for the now archived NGINX Wiki section of https://www.nginx.com

rr

* * *

From: “Harmer, Sean” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
Sent: Wednesday, April 07, 2010 10:09 PM  
To: [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
Subject: Dynamic Redirects

> NGINX Team,
> 
> Is there a way to capture the User-Agent from an HTTP Header, then query a  
> database for that User-Agent, then based on the result of the database  
> query, transparently route the request to an appropriate server?
> 
> Thanks,  
> Sean

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [July 27, 2011, 4:39am UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/7 "2011-07-27T04:39:40Z")

</div>

Ezra,

Tried following the steps and configuration you provided, but I keep  
getting an empty reply from the server when I hit it. I can that the  
variable $answer is properly set to the expected value (something like  
[http://some-ip](http://some-ip)), but for some reason it doesn’t work. It hangs for as  
long as the keepalive\_timeout is set to.

Any pointers as to why this doesn’t work would be highly appreciated.  
Really like the idea of using redis for this purpose.

Thanks

Posted at Nginx Forum:

> **[Re: Dynamic Redirects](https://forum.nginx.org/read.php?2,72415,212973#msg-212973)**
>
> NGINX Team, Is there a way to capture the User-Agent from an HTTP Header, then query a database for that User-Agent, then based on the result of the database query, transparently route the request to an appropriate server? Thanks, Sean \_\_\_\_\_\_\_\_\_\_\_\_\_\_

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [July 27, 2011, 5:48am UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/8 "2011-07-27T05:48:50Z")

</div>

On Thu, Apr 8, 2010 at 3:09 AM, Harmer, Sean [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> NGINX Team,
> 
> Is there a way to capture the User-Agent from an HTTP Header, then query a  
> database for that User-Agent, then based on the result of the database  
> query, transparently route the request to an appropriate server?

I’ve got a sample app running to demonstrate this, using our  
opensource modules ngx\_redis2, ngx\_lua, and ngx\_set\_misc:

```
https://github.com/agentzh/redis2-nginx-module

http://github.com/chaoslawful/lua-nginx-module

https://github.com/agentzh/set-misc-nginx-module

```

Here’s the complete code listing for my nginx.conf:

```
worker_processes 1;
error_log logs/error.log info;
events {
    worker_connections 1024;
}
http {
    upstream apache.org {
        server 140.211.11.131;
    }

    upstream nginx.org {
        server 206.251.255.63;
    }

    server {
        listen 8080;

        location = /redis {
            internal;
            set_unescape_uri $key $arg_key;
            redis2_query get $key;
            redis2_pass 127.0.0.1:6379;
        }

        location / {
            set $target '';
            access_by_lua '
                local key = ngx.var.http_user_agent
                local res = ngx.location.capture(
                    "/redis", { args = { key = key } }
                )

                print("key: ", key)

                if res.status ~= 200 then
                    ngx.log(ngx.ERR, "redis server returns bad 

```

status: ",  
res.status)  
ngx.exit(res.status)  
end

```
                if not res.body then
                    ngx.log(ngx.ERR, "redis returned empty body")
                    ngx.exit(500)
                end

                -- we can use the lua-redis-parser library to

```

parse the reply  
– here instead  
local server = string.match(res.body,  
“^$%d+\r\n(%S+)\r\n”)  
if not server then  
ngx.log(ngx.ERR, "bad redis response: ",  
res.body)  
ngx.exit(500)  
end

```
                print("server: ", server)

                ngx.var.target = server
            ';

            proxy_pass http://$target;
        }
    }
}

```

This version is longer than the one using ngx\_eval, but with much  
saner error handling.

And then let’s start the redis server on the localhost:6379:

```
./redis-server # default port is 6379

```

and feed some keys into this using the redis-cli utility:

```
$ ./redis-cli
redis> set foo apache.org
OK
redis> set bar nginx.org
OK

```

And then let’s test our nginx app!

```
$ curl --user-agent foo localhost:8080
<apache.org home page goes here>

$ curl --user-agent bar localhost:8080
<nginx.org home page goes here>

```

To further tune the performance, one could enable the connection pool  
for the redis connections, as documented in ngx\_redis2’s README.

You can consider using the ngx\_openresty bundle to run this sample to  
eliminate all the pain involved in downloading and enabling 3rd-party  
modules by yourself 😉

```
http://openresty.org/

```

Regards,  
-agentzh

---

<div class="post-metadata">

**Author:** ![HarmerS\_Sean](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/h/b487fb/32.png) [@HarmerS\_Sean](https://www.ruby-forum.com/u/HarmerS_Sean)\
**Post date:** [July 27, 2011, 6:17am UTC](https://www.ruby-forum.com/t/dynamic-redirects/186488/9 "2011-07-27T06:17:50Z")

</div>

On Thu, Apr 8, 2010 at 5:27 AM, Reinis R. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> While you can prolly try use this  
> [GitHub - openresty/drizzle-nginx-module: an nginx upstream module that talks to mysql and drizzle by libdrizzle · GitHub](http://github.com/chaoslawful/drizzle-nginx-module) (although iirc the  
> possibility to get a single return value as variable back is still in TODO)

You can use the ngx\_rds\_json module to emit JSON from the RDS reply  
generated by ngx\_drizzle (and ngx\_postgres), and use a decent lua JSON  
library like lua-cjson or lua-yajl to parse it into Lua data  
structures and proceed processing.

> I would imagine that using some dynamic language (like php or even the  
> embeded perl (if you really want to do this on server side)) is more simple  
> and while client is being redirected its still more or less transparent to  
> him.

PHP and the Embedded Perl addon are all blocking on backend requests  
to mysql/postgres/redis/whatever. The penalty and impact of the latter  
is more serious if one does not start _many_ nginx worker processes.  
Well, anyway, if one doesn’t care about performance, especially  
concurrency, then whatever methods can be used 🙂

Regards,  
-agentzh
