HAProxy: Forward based on string in url combined with existing acl

Written by - 3 comments

Published on - Listed in Linux HAProxy


I got a special request to configure a HAProxy load balancer to use a different backend when a certain string was found in the URL.
There are already some very good answers how to do that (for example http://serverfault.com/questions/127491/haproxy-forward-to-a-different-web-server-based-on-uri) but I had to adapt the config for my environment which includes a combination of already existing acl's.

To show the current architecture:

test.example.com -> backend1 -> internalserver1:8888
stage.example.com -> backend2 -> internalserver2:8888

Because the HAProxy listens on the same IP address, both domains test.example.com and stage.example.com are handled by the same HAProxy. But depending on which domain was called, the requests should be forwarded to a different backend.
To be able to handle different domains, an acl which checks the requested host address does the job:

frontend main_http
    bind *:80

    # Define ACL based on host names
    acl test_host hdr(host) -i test.example.com
    acl stage_host hdr(host) -i stage.example.com

    # Default Route to normal backends
    use_backend backend_test_8888 if test_host
    use_backend backend_stage_8888 if stage_host

The ACL's "test_host" and "stage_host" check which host name was requested by using hdr(host). If it matches test.example.com, the acl "test_host" is set to true.
Depending which ACL is true (e.g. which host name was requested), a different backend is used (backend_test_8888 or backend_stage_8888).

Now I need an additional ACL which handles a special string (blog) in the requested URL. These requests should be sent to another backend.

test.example.com -> backend1 -> internalserver1:8888
test.example.com/bla/blog/page3 -> backend3 -> internalserver1:8889
stage.example.com -> backend2 -> internalserver2:8888
stage.example.com/bla/blog/page3 -> backend4 -> internalserver2:8889

In the serverfault.com example from above the answer was to use "path_beg" but that doesn't work for my case because I need to make sure that the string is not only at the beginning of the URI, it could appear literally anywhere in the whole URI.

To solve this, I created the following ACL which uses the sub(-string) rather than beg lookup method:

    # Define ACL based on string in URI
    acl blog_in_uri path_sub blog

So the ACL "blog_in_uri" is set to true, as soon as the string "blog" appears in the requested URI.

How to combine these two ACL's now? First I needed to create separate backends to handle the different destination ports:

#---------------------------------------------------------------------
# backend_test_8889
#---------------------------------------------------------------------
backend backend_test_8889
    balance     source
    server      intenralserver1 192.168.44.10:8889 check inter 2000 fall 1 rise 3
    server      intenralserver11 192.168.44.11:8889 check inter 2000 fall 1 rise 3

#---------------------------------------------------------------------
# backend_stage_8889
#---------------------------------------------------------------------
backend backend_stage_8889
    balance     source
    server      intenralserver2 192.168.44.20:8889 check inter 2000 fall 1 rise 3
    server      intenralserver22 192.168.44.22:8889 check inter 2000 fall 1 rise 3

Then I combined the ACL's in the "use_backend" syntax. Important is the following information from the HAProxy config documentation:

There may be as many "use_backend" rules as desired. All of these rules are evaluated in their declaration order, and the first one which matches will assign the backend.

This means that in my scenario I have to use the ACL combination (hostname + blog in uri) first. If it doesn't match, it will use the second ACL (hostname only).
To combine two or more ACL's just write both one after another. HAProxy will understand this as logical AND (do not add the word ADD into the config, this is considered a syntax error!):

frontend main_http
    bind *:80

    # Define ACL based on host names
    acl test_host hdr(host) -i test.example.com
    acl stage_host hdr(host) -i stage.example.com

    # Define ACL based on string in URI
    acl blog_in_uri path_sub blog

    # Special route which matches both hostname and string acl
    use_backend backend_test_8889 if test_host blog_in_uri
    use_backend backend_stage_8889 if stage_host blog_in_uri

    # Default Route to normal backends
    use_backend backend_test_8888 if test_host
    use_backend backend_stage_8888 if stage_host

Now all the requests which contain the string "blog" in the URI will be forwarded to a different backend rather than using the default backend for the requested domain.


Add a comment

Show form to leave a comment

Comments (newest first)

Levente Peres from Demecser, Hungary wrote on Sep 30th, 2017:

Simple and elegant solution.

I had a very similar problem, and this helped me greatly.

Thank you!!


MikeJ from wrote on Jun 7th, 2017:

thanks, still totally relevant in 2017! solved my problem completely.


Rodolfo from Portugal wrote on Feb 7th, 2017:

Amazing Solution!

Thank you!


RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder