HAProxy – How to Configure as a Reverse Proxy

haproxyreverse-proxy

I have multiple web servers, hosting multiple domains. Occasionally, I need to move a domain from one web server to another. Rather than have to update the DNS for the domain to point at the new server location, I would like to set all the domains to point to a reverse proxy (?) that will then pass web requests on to the correct servers. I am trying to use haproxy to achieve this, without much success.

as an example:

There are two servers, web1.foo.com and web2.foo.com. web1 serves sites for abc.com and def.com. web2 serves sites for cba.com and fed.com.

there is also a proxy server, proxy.foo.com.

dns for abc.com, etc are pointed at proxy.foo.com, which is configured to map domains to servers like so:

abc.com -> web1.foo.com
def.com -> web1.foo.com
cba.com -> web2.foo.com
fed.com -> web2.foo.com

the problem is, I cant get it to work using haproxy… it is trying to load balance instead of reverse proxy… if I go to abc.com, it will cycle between web1 and web2.

so, I guess I am wondering, is this even possible with haproxy, and if so, where am I going wrong with the configuration? my config looks like this:

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # Default SSL material locations
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private

    # Default ciphers to use on SSL-enabled listening sockets.
    # For more information, see ciphers(1SSL). This list is from:
    #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
    ssl-default-bind-options no-sslv3

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

frontend localhost
    bind *:80
    bind *:443
    option tcplog
    mode tcp
    default_backend servers

backend servers
    mode tcp
    server abc.com web1.foo.com
    server def.com web1.foo.com
    server cba.com web2.foo.com
    server fed.com web2.foo.com

Best Answer

Yes, the way you have things configured will essentially load balance all incoming connections between the backend servers.

What you want is basically one backend definition for each real web server. I think the gist of what you want is multiple backend definitions, one for each real web server. Then in your frontend section, you'd want to use various conditionals to use the associated backend for whichever frontend URL was requested. A pseudo-config might look something like this:

frontend localhost
    mode http
    use_backend web1 if { hdr(host) -i abc.com }
    use_backend web1 if { hdr(host) -i def.com }
    use_backend web2 if { hdr(host) -i cba.com }
    use_backend web2 if { hdr(host) -i fed.com }

backend web1
    server web1 web1.foo.com

backend web2
    server web2 web2.foo.com

Note the mode http. I believe that's required to actually do things like make decisions based on HTTP headers.

And when it's time to move what URLs go to what backend, it's a simple matter of updating the config file and doing a soft reload to haproxy.