Nginx's map directive allows for cleaner config

I didn’t start with Nginx, but it’s become my standard for open source load balancing now-a-days. I started with HAProxy - I remember first hearing about it in my first job, in 2008, when our CTO at the time was raving about it - “HAProxy rocks”, he would say. I didn’t get a chance to use it until several years later. It was when I first started load balancing services properly - somewhat of an improvement than having many services listen publicly on various ports.

At the same time, I’ve been using good old trust Apache HTTPD since the late 90s. I remember that it took me ages and ages to configure it originally, but I built up so much muscle memory to set it up over the years.

Nowadays, I don’t hear about my beloved HAProxy and HTTPD anymore, and it seems like the world has moved on to Nginx for load balancing, serving static web content AND hosting good old PHP! It wasn’t love at first sight, however. My first ever experience of Nginx was very horrible, in about 2019, when I was first introduced to an early version of lancache. The configuration syntax really threw me - I knew nothing like it, and it was very difficult to understand.

However, over the years, it’s clear that Nginx dominated the Kubernetes space as an ingress controller, and it’s configuration syntax really is lightweight and easy to understand.

Anyway, this blog post is about a server that I wanted to migrate, from an old HAProxy instance, with sprawling repetitive config, to Nginx which I standardised on many years ago.

In particular, the “map” directive in in Nginx config is really powerful, and allows you to do a lot with very little code.

Let’s look at a comparison of before - with HAProxy, and after - with Nginx.

Original HAProxy config:

frontend https
	bind *:443 ssl crt /etc/letsencrypt/live/demo.jread.com/combined.pem

    mode http
    option httplog
    redirect scheme https code 301 if !{ ssl_fc }

	use_backend spooncheck if { hdr(host) -i spooncheck.demo.jread.com }
	use_backend japella if { hdr(host) -i japella.demo.jread.com }
	use_backend stencilbox if { hdr(host) -i stencilbox.demo.jread.com }
	use_backend uar if { hdr(host) -i uar.demo.jread.com }
	use_backend faridoon if { hdr(host) -i faridoon.demo.jread.com }
	use_backend olivetin if { hdr(host) -i olivetin.demo.jread.com }
    ... many more lines ...

backend spooncheck
    mode http
    option httplog
    server spooncheck 127.0.0.1:8080 check

backend japella
    mode http
    option httplog
    server japella 127.0.1:8081 check

backend stencilbox
    mode http
    option httplog
    server stencilbox 127.0.1:8082 check


... many more lines ...

This configuration is fine, it works, but it’s very verbose and repetitive. There’s probably 12+ services simply running on different ports and this creates a lot of duplicate config.

However, using the map directive, we can simplify that massively in Nginx. Compare the same configuration from HAProxy, ported to Nginx:

Nginx config using map directive:

map $host $backend {
	default								127.0.0.1:8080;
	stencilbox.demo.jread.com			127.0.0.1:8080;
	cookingtimer.demo.jread.com			127.0.0.1:8086;
	faridoon.demo.jread.com				127.0.0.1:8083;
	japella.demo.jread.com				127.0.0.1:8082;
	olivetin.demo.jread.com				127.0.0.1:1337;
    ... many more lines ...
}

server {
    server_name demo.jread.com;              # Define domain names
    root /var/www/demo.jread.com/;           # Set the document root
    index index.html index.htm;              # Specify default index files

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/demo.jread.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/demo.jread.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

	location / {
		proxy_pass http://$backend;
	}
}

Nice! This configuration is way more concise and is easier to understand. Nginx’s map directive is really powerful, and allows you to do a lot with very little code.

About James Read

Picture of James Read James is a "full stack" Open Source enthusiast, who enjoys creating no-nonsense open source software.

Dad, hobbyist developer, open-source enthusiast and Red Hatter.