Set up HAProxy reverse proxy and load balancer

Here you will install and configure HAProxy as a reverse proxy for your applications. With the provided configuration, it will only proxy requests from the client to NGINX. An example configuration for handling Node.js applications is also provided, but any further customization depends on the specific project.

1 Install

1.1 If using macOS with MacPorts

sudo port install haproxy

2 Configure

2.1 Create configuration file

Create configuration file /opt/local/etc/haproxy/haproxy.cfg with the following content:

defaults
    mode http

    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

    option  httplog

frontend local
    mode http

    bind *:80
    bind *:6080
    bind *:443   ssl crt /Users/brodijak/ssl/chain.pem
    bind *:6443  ssl crt /Users/brodijak/ssl/chain.pem

    acl is_node req.fhdr(Host),map_str(/opt/local/etc/haproxy/node_domains_ports.map) -m found
    acl is_node_pass_through path_reg -i -f /opt/local/etc/haproxy/node_pass_through.patterns

    http-request capture req.hdr(Host) len 128
    http-request capture req.fhdr(Referer) len 128

    http-response set-header x-haproxy-frontend local
    http-request set-header x-forwarded-proto https if { ssl_fc }

    option forwardfor

    use_backend nginx   if { path -m beg /.well-known/ } { dst_port 80 }
    use_backend nginx   if { path -m beg /.well-known/ } { dst_port 443 }
    use_backend varnish if { path -m beg /.well-known/ } { dst_port 6080 }
    use_backend varnish if { path -m beg /.well-known/ } { dst_port 6443 }

    use_backend node    if is_node !is_node_pass_through

    use_backend nginx   if { dst_port 80 }
    use_backend nginx   if { dst_port 443 }
    use_backend varnish if { dst_port 6080 }
    use_backend varnish if { dst_port 6443 }

    default_backend nginx

backend node
    http-request set-dst-port req.fhdr(Host),map(/opt/local/etc/haproxy/node_domains_ports.map)
    http-response set-header x-haproxy-backend node
    server node 127.0.0.1:0 maxconn 32

backend nginx
    http-request set-path '%[path,regsub(^/ngcontentapi"(/|$)",/,)]'
    http-response set-header x-haproxy-backend nginx
    server nginx 127.0.0.1:8080 maxconn 32

backend varnish
    http-response set-header x-haproxy-backend varnish
    server varnish 127.0.0.1:6081 maxconn 32

Make sure to adapt the paths to certificate chain file on your system.

Create port map file /opt/local/etc/haproxy/node_domains_ports.map with the following content:

# Contains a list of domains handled by Node.js, mapped to a corresponding port
# on which Node.js app is running

example.dev.php82.ez    3000
us.example.dev.php82.ez 3000

Create file containing pass-through patterns /opt/local/etc/haproxy/node_pass_through.patterns with the following content:

# Contains regular expression patterns to match URLs that are found on Node.js domains,
# but should be handled by Varnish/PHP instead of Node.js (passed through to PHP)

# API endpoints
^/(en/|fr/|de/|hr/)?(api|ngopenapi)
^/ngcontentapi

# Admin
^/adminui
^/graphql

# Assets
^/bundles
^/assets
^/var

# Debug
^/_wdt

# Sitemaps and robots.txt
^/sitemap/.*
^/robots.txt

3 Start

3.1 If using macOS with MacPorts

sudo port load haproxy

That will also configure the service to start automatically after a reboot.

5 Test

Execute on the command line:

curl -I phpinfo.php82

You should receive output similar to:

HTTP/2 307
server: nginx/1.26.3
date: Wed, 12 Mar 2025 06:08:26 GMT
content-type: text/html
content-length: 171
location: https://phpinfo.php82:8080/
x-haproxy-backend: nginx
x-haproxy-frontend: local

Make sure the following lines are present:

x-haproxy-backend: nginx
x-haproxy-frontend: local

6 Logging

6.1 If using macOS with MacPorts

To see HAProxy logs on macOS, you need to stop the HAProxy service and run it in the foreground with debug mode enabled. This way, logs will be displayed directly in the terminal:

sudo port unload haproxy
haproxy -f /opt/local/etc/haproxy/haproxy.cfg -d -V