If you host dozens of web services that reside at various subdomains, TCP ports, and paths, then migrating them to live under a single address could simplify how clients access them and make your job of managing access easier. It would mean moving from a hodgepodge of address schemes, such as:

  • www.example.com/app-a/
  • www.example.com:8080/app-b/
  • app-c.example.com

to a single address wherein services are designated by the URL’s path:

  • www.example.com/app-a/
  • www.example.com/app-b/
  • www.example.com/app-c/

The good news is that you don’t need to rearrange your entire network to make this happen. You can deploy the HAProxy load balancer in front of your services and then use path-based routing to direct requests to the correct backend service. Here’s how to do it.

Configure HAProxy for path-based routing

In the end, your HAProxy configuration will look like this:

Let’s dig into what it all means. Consider the frontend section, which receives requests. It contains a use_backend directive that directs traffic for each application.

The first line states that if the path is /a or if it begins with /a/ then route to the backend named app-a. If the path is /b or if it begins with /b/ then route to the backend named app-b. The path fetch method compares the whole path, while path_beg compares the beginning of the path. These lines route requests to a specified backend pool of servers when the given condition is true.

For example, the first line will match requests such as:

  • www.example.com/a
  • www.example.com/a/my-function

But it will not match:

  • www.example.com/aaa
  • www.example.com/abbb/my-function

If you would like to match on other prefixes, simply make a list of them:

This syntax can be a bit terse. So if you prefer a more verbose option, you can break out the conditional expression onto a separate line or lines. The syntax below has the same result, but explicitly defines the condition using two acl directives. By having two such directives with the same name, they form a single expression separated by a logical or:

Did you know? You can also use map files to store long lists of path-to-backend mappings.

Next, define the backend sections.

In this example, the http-request replace-path directives remove the prefixes /a and /b before relaying the request to the server. A request for the URL path /a/my-function would become just /my-function before reaching the server. It’s important that this directive goes into the backend and not the frontend because if it were in the frontend, it would replace the path, stripping off the prefix, before the use_backend rules could try to match it, resulting in no match. In general, HAProxy applies http-request rules before use_backend rules. Therefore, it’s better to route the request first, then apply such transformations.

The http-request replace-path directive expects a regular expression, such as /a(/)?(.*), which states that you want to match any path that begins with /a optionally followed by a forward slash, and then followed by anything else. The anything else part, enclosed in parentheses, is captured, meaning you can refer to it in the replacement value by number. In this instance, the anything else we captured is referenced with \2, since it is the second capture group (second set of parentheses).

Below are a few ways in which HAProxy will route requests and how the backend servers will see them:

Original requested path Routes to server Server sees
/a/ app-a /
/b app-b /
/a?foo=bar app-a /?foo=bar
/b/my-function?foo=bar app-b /my-function?foo=bar

Conclusion

In this blog post, you learned how to configure path-based routing using HAProxy. Several key points to remember: define conditions for which application to route the request to by using the path and path_beg fetch methods to match the path, and you can strip off the prefix before the request is relayed to the server by using the http-request replace-path directive.

SHARE THIS ARTICLE