Published: 13. 9. 2019   Category: GNU/Linux

NGINX redirection based on user name/authentication

This is a minimal example of how to handle redirection to different HTTP servers based on the provided user name. I have solved this problem for an environment which should be accessed via one hostname but two different applications running behind the curtain. Each of these applications is providing the same REST API and each user should use its instance and data. Also, authentication and password check is checked by applications themselves and HTTPS is used. But because I would like to concentrate just to provide redirection I have kept these NGINX's configuration files as simple as possible.

Example configuration of static HTTP servers

These two servers are residing on different TCP ports 8000 and 9000. To see which one is answering, each server returns a different static HTML page. I have defined this static page directly in NGINX's configs and in case you need some slightly bigger HTML page, the page is stored in variable $html_page and I am using variable concatenation because I wanted to have page code string split into several lines. It is also possible to add some binary data via base64, for example PNG image: <img border="0" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEU..." />. This may be handy if you need a server providing just one static page (for example downtime information) without spending more time to set up different files, web root, etc.

Note: All configuration blocks below may be saved in the one file redir_test.conf.

Server A.

server {
    listen 8000;

    location / {
        add_header Content-Type text/html;
        set $html_page '<html>\n';
        set $html_page '${html_page}<h1>Hello! This is the first HTTP server!</h1>\n';
        set $html_page '${html_page}</html>\n';
        return 200 $html_page;
    }
}

Server B.

server {
    listen 9000;

    location / {
         add_header Content-Type text/html;
         set $html_page '<html>\n';
         set $html_page '${html_page}<h1>Ahoy! This is the second HTTP server!</h1>\n';
         set $html_page '${html_page}</html>\n';
         return 200 $html_page;
    }
}

Put both config file into your preferred NGINX configuration directory. They are different on different systems or Linux distributions. Ubuntu has its in /etc/nginx/sites-enabled (symlink from /etc/nginx/sites-available) and RPM based distros (Fedora, CentOS, RHEL) in /etc/nginx/conf.d/.

You can restart nginx now and test if both servers are answering in a browser or via CLI:

$ curl localhost:8000
<html>
<h1>Hello! This is the first HTTP server!</h1>
</html>
$ curl localhost:9000
<html>
<h1>Ahoy! This is the second HTTP server!</h1>
</html>

Default server working as a proxy

This is the main server sitting on port 80 and redirecting traffic to different local ports. There is a very handy map directive which will use first string/value in variable and it will assign another value into a different variable. In the example below, the user Alice is redirected to server A. and user Bob to server B. The variable $target holds information about target location.

However, NGINX cannot put location-block inside if-block so each if-block contains the definition of other variables which will be later used for different environments. The example has only $target_port, but in the real world, you may use a different variable to modify HTTP headers or requests here.

Another trick is using regular expression for the situation when $target is empty and in this case, the server A. is be used as a fall-back (NGINX does not have any boolean expressions and also no else-block). Another option is to use if-block with $target = '' to return 401 (unauthorized).

# User environment mapping definition
map $remote_user $target {
   Alice server_a;
   Bob   server_b;
}

server {
    listen 80;

    if ($target ~ "server_a|^$") { # server_a or regex for empty string
       set $target_port 8000;
    }

    if ($target = "server_b" ) {
       set $target_port 9000;
    }

    location / {
        proxy_pass http://127.0.0.1:$target_port;
    }
}

Restart server and send HTTP request with curl you can provide authentication with CLI option -u user:password, but password is empty because there is no authentication enabled in this example.

$ curl -u Alice: http://localhost
<html>
<h1>Hello! This is the first HTTP server!</h1>
</html>
$ curl -u Bob: http://localhost
<html>
<h1>Ahoy! This is the second HTTP server!</h1>
</html>