Category Archives: Nginx

Using the map directive to resolve multiple conditions in Nginx

As your Nginx configuration expands and becomes more complex, you will inevitably be faced with a situation if which you have to apply configuration directives on a conditional basis.

Nginx includes an if directive, but they really don’t want you to use it: https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/

The alternative that is generally recommended is the map directive, which is super-efficient as map directives are only evaluated when they are used: http://nginx.org/en/docs/http/ngx_http_map_module.html

This isn’t quite as intuitive as the if directive, and newcomers to Nginx can struggle with its logic.

Basically put, map will create a new variable, and assign the value of that variable based on the value of other variables. eg

$new_variable = 1 if $existing_variable = true

or

$new_variable = 0 if $existing_variable = false.

map $existing_variable $new_variable {
    "true" 1;
    "false" 0;
}

You can leverage this in conditional configuration by assigning a configuration value to a new variable, and using that in your configuration. For example, use a different whitelist depending on the source ip address of the request:

map $remote_addr $whitelist {
    "192.168.100.1" "whitelist1.incl";
    "192.168.100.2" "whitelist2.incl";
}

location / {
   ...
   include $whitelist;
   ...
}

This works fine when you you want to set the value of a new variable based on evaluation of one other variable, but in a typical if statement, you can evaluate multiple variables at the same time:

if ( $a == 1 && and $b == 2) etc etc

Can you do the same thing with map?

Some people will tell you can’t, which is technically true, but you can “couple” map blocks to produce the same effect. Let’s use the following example:

If a request is for host "example.com" and the source ip address is 192.168.100.1, return a different home page.

If this instance, we need 2 map blocks:

  • One to test if the host name is “example.com”
  • One to test if the source ip address is 192.168.100.1

and 2 variables:

  • One to hold the value of the home page file ($index_page)
  • One to link the map conditions together ($map_link)
#Test the host name, and assign the home page filename value to the $map_link variable
map $host $map_link {
    default "index.html";
    "example.com" "index_new.html";
}

#Test the source address, and if it matches the relevant address, interpolate the value assigned from the previous map
map $remote_addr $index_page {
    default "index.html";
    "192.168.100.1" "${map_link}";
}

location / {
    ....
    index $index_page;
    ....
}

The full logic here is as follows:

If the hostname is “example.com”, we should provisionally make the home page file “index_new.html”. Otherwise, the home page file should be “index.html”.

If the source ip address is 192.168.100.1, which is the second part of our test, we should refer to the result of the first part of our test. Otherwise, we can ignore the first part of our test and use the default value “index.html”.