HAProxy is one of very few pieces of software that are truly a joy to use. It’s written in C, it’s reliable, fast, secure and versatile.
I’ve been using it for a number of years now for all kinds of purposes and its ability to inspect and manipulate HTTP requests is one of the features I use most often.
In this tutorial, I won’t explain how to install and configure HAProxy, but rather how to use HAProxy for load balancing or simply redirecting HTTP traffic to desired backends by matching specific cookies or request headers. The cherry on the top will be dynamic HAProxy backends defined in map files, for easier automation and cleaner configuration file.
For this scenario, lets assume that we have an frontend named fe_main
and a couple of backends - be_alpha
, be_bravo
, be_charlie
and be_delta
as default backend.
Our fe_main
frontend handles a bunch of RESTful API requests and based on X-API
request header value, we’ll let HAProxy decide where to pass those requests. If the request doesn’t have such header, it’ll be forwarded to default HAProxy backend (be_delta
).
To keep haproxy.cfg
configuration file as clean as possible, we’ll map X-API
values with backends in separate, map
file. Thanks to HAProxy’s powerful, but still easy to read configuration language, we can achieve all of the above in just a few lines.
Example of /etc/haproxy/haproxy.cfg
frontend fe_main
bind 192.168.1.100:80
mode http
# check the presence of X-API request header
acl xapi_header req.hdr(X-API) -m found
# if X-API header exists, check the backend to which the request needs to be forwarded
# use be_delta if there's no match
use_backend %[req.hdr(X-API),lower,map(/etc/haproxy/api.map,be_delta)] if xapi_header
# if X-API header doesn't exist use default backend
default_backend be_delta
# backends
backend be_alpha
server alpha_server 192.168.1.200:80
backend be_bravo
server bravo_server 192.168.1.201:80
backend be_charlie
server charlie_server 192.168.1.202:80
backend be_delta
server delta_server 192.168.1.203:80
The /etc/haproxy/api.map
map file looks very simple. It contains space-delimited key-value pairs. The value of X-API
header is key and the HAProxy backend which should handle the request is the value.
Example of /etc/haproxy/api.map
# X-API value | HAProxy backend
v1.0 be_alpha
v1.1 be_bravo
v2.5 be_charlie
v2.2 be_alpha
v1.1 be_delta
So, if the request has X-API: v1.1
header, that request will be forwarded to be_bravo
backend. The request with X-API: v2.2
will end up at be_alpha
backend and so on. If there’s no match in the map file, be_delta
backend will be used.
If we wanted to inspect an cookie, instead of request header, pretty much the whole configuration remains the same:
frontend fe_main
bind 192.168.1.100:80
mode http
# check the presence of APIcookie request header
acl api_cookie hdr_sub(cookie) -i APIcookie -m found
# if APIcookie cookie exists, check which backend should handle the request
# use be_delta if there's no match
use_backend %[req.cook(APIcookie),lower,map(/etc/haproxy/api.map,be_delta)] if api_cookie
# if APIcookie doesn't exist, use default backend
default_backend be_delta
# backends
backend be_alpha
server alpha_server 192.168.1.200:80
backend be_bravo
server bravo_server 192.168.1.201:80
backend be_charlie
server charlie_server 192.168.1.202:80
backend be_delta
server delta_server 192.168.1.203:80
Pretty simple, right? Just make sure that you restart/reload HAProxy when you modify the map file, as HAProxy won’t re-read it automatically.
For detailed information about configuration directives used in these examples, take a peek at official HAProxy documentation.