HAProxy path-based routing with SSL in http mode with SNI on backend servers

The setup I need to build with HAProxy is the high availability solution consisting of http (http to https redirection must be configured in HAProxy) and https frontends with two backends. Backend servers have HTTPS enabled so HAProxy must establish HTTPS connection to backend servers. We have four backend servers and we want first two servers serve one specific app (/app path) and the other two – all other requests so we need path-based routing in HAProxy. In addition, there are multiple domains that must be served by backend servers (and HAProxy).

As we need path-based routing, HAProxy must operate in http mode (on the 7th level) allowing it to examine contents of HTTP headers and extract Host and Path headers. Later, the incoming request must be sent to a proper backend based on the Path header and connection to backend server must be established via HTTPS. As multiple domains must be served by HAProxy and backends we need to install multiple certificates in HAProxy and enable Server Name Indication (SNI) with backend servers.

Here is how configure HAProxy in that way step by step.

So, first things first. Let’s create HTTP frontend and redirect all HTTP traffic to HTTPS.

Redirect all HTTP traffic to HTTPS in HAProxy

We need to redirect all incoming HTTP traffic (port 80) to HTTPS (port 443). To do that we define a frontend and use an ACL to detect the HTTP protocol and redirect using the http-request directive.

Let’s created HTTPS enabled frontend that must serve multiple domains with their corresponding certificates.

HTTPS frontend with multiple domains and certificates in HAProxy

There are three options here:

  • Use two crt options when binding.
  • Concatenate multiple certs in just one PEM file.
  • Use crt with a path to a folder containing all the certs.

I chose the last option. For that, I created “certs” folder in /etc/haproxy and put all pem certificates with corresponding private keys here. Initially, I’d had the certificates in pfx format so I had to convert pfx to pem

As a result, in /etc/haproxy/certs folder I had 2 certificates (one of them was wildcard) and two private keys. Please note that you must specify private key filename as certificate's name + .key extension.

And in HAProxy config we have:

Next, let’s define simple path-based routing.

Path-based routing in HAProxy

To be able to do path based routing HAproxy must be able to examine HTTP headers, that’s why we configured TLS termination in a previous step. I want all paths beginning with /app/ to be served by “app” backend and all other requests should be served by “other” backend. Here is a simple rule we should add:

Let’s create our backends and enable SNI for backends.

Create HTTPS backends with SNI in HAProxy

HAProxy must connect with HTTPS to backend servers that serve multiple domains. So we must pass domain name (Host header) to backend servers so they process our request properly. Without SNI in backend we would get 503 error from backend servers. Here is a configuration:

We have specified two backends: “app” for paths that begin with /app/ and “other” for all other requests. Please notice that frontends and backends should be in http mode. Next, I wanted to implement sticky session in HAProxy using cookie SERVERUSED.

If the user was first relayed to the web1 server, then their cookie will contain the value web1 and HAProxy will know to keep sending them there. The insert parameter creates the cookie, indirect removes the cookie on each incoming request before forwarding the message to the server, and nocache sets the Cache-Control: private HTTP header so that cache servers between HAProxy and the user won’t cache the response.

Next, I set type of load balancing to “roundrobin” so requests will be distributed equally between backend servers and, finally I define backend servers with “server” directive. Here I specify server’s name for HAProxy, IP address and port for HAProxy to connect to, tell HAProxy that backend servers use SSL (“ssl” directive) but HAPRoxy should not verify backend server’s certificate (“verify none“). “maxconn 30” allows to limit number of connections and, finally I define SNI options: HAProxy extracts target domain name from “Host” header and sends it to backend server so backend server how which virtual host should process the request. Last but not least I defined SERVERUSED cookie’s value to server’s name for HAProxy sticky session to work.

Now it’s time to put it all together:

And that’s it! You now have level 7 HAProxy load balancer with TLS termination, path-based routing, SNI and sticky sessions!
Good luck!

Want me to do this for you? Drop me a line: itgalaxyzzz {at} gmail [dot] com