0.00

Load Balancing with HAProxy

Image
Server Architecture: using HAProxy for balancing web services and support HTTP1.1, HTTP2 and HTTP3

About HTTP/HTTPS Load Balancing and Proxy servers

Load balancing enables you to distribute work to a group of servers that host identical copies of your web site or web / cloud application via the HTTP/HTTPS. A proxy is just a middleware between clients and a backend server. HAProxy supports any of the above scenarios being a very reliable choice, supporting HTTP1.1 / HTTP2 (over TCP) and HTTP3 (over UDP - QUIC), realistic rate limit and efficient distribution of requests.
HAProxy load balancer supports 3 algorithms: round-robin, least-connections and hash-based as a standard. The horizontal scaling and load balancing is the idea that operating multiple servers will help keep load at manageable levels on each server. But not only, because using HAProxy as a load balancer or proxy middleware will improve many things such as memory resources, being abe to implement a more realistic rate limit protection and serving all supported HTTP protocols at once.

Load Balancing Architecture

Using Apache / PHP Web Server with HAProxy as a load balancer or just proxy to serve all HTTP1.1 / HTTP2 / HTTP3 at once

First of all, Apache stable branch (2.4) does not yet support HTTP3. For HTTP2 it can use MPM Worker or MPM Event but not the MPM Prefork because the prefork module is not designed to serve many resources on the same request from a client as supported by standard in HTTP2.
A wide common setup is when using Apache with PHP as module with MPM Prefork which stands on HTTP1.1 as a standard in Apache 2.4 and PHP7 or PHP8. But this scenario can apply as well when using Apache with PHP as FPM/FCGI. And this scenario can be extended to other similar Apache server-side scripting architectures, other than PHP.
HAProxy starts offering HTTP3 starting with version 2.6. If you have multiple servers, setting HAProxy as a load balancer and proxy would be a better scenario. If you only have just one server, using HAProxy as a proxy between clients and Apache/PHP is also a good enough scenario for your web architecture.
With Apache behind HAProxy is simple to implement any of the above scenario by only enabling mod_remoteip and moving apache from port 80 and 443 to a different port (in this scenario the port 80 and 443 will be used by HAProxy).


Steps to use HAProxy as a simple proxy between Apache

  • Install HAProxy on the same server where Apache is running
  • Change Apache ports to something different than 80 / 443 (let's say 12080 for HTTP and 12443 for HTTPS) and keep the virtual hosts as they are with the exact IP addresses as they were before for Apache to correct solve the mapping of IP / Host Name as before
  • Enable mod_remoteip in Apache to solve the correct REMOTE_ADDR of the visitor because without this the REMOTE_ADDR will be instead the IP of HAProxy which is wrong
  • Block the ports 12080 and 12443 in your firewall to be accessible only by local HAProxy from all IP addresses that it will serve
  • Configure HAProxy frontend to listen to the port 80 and 443, serve HTTP1.1, HTTP2 and HTTP3
  • Configure HAProxy backend to serve from Apache that runs on ports 12080 and 12443
  • Set in HAProxy a reasonable rate limit policy to protect against DDOS attacks

Steps to use HAProxy as a load balancer for multiple Apache servers

  • Install HAProxy on all servers where Apache instances are running
  • On each server from your cluster: change the Apache ports to something different than 80 / 443 (let's say 12080 for HTTP and 12443 for HTTPS) and keep the virtual hosts as they are with the exact IP addresses as they were before for Apache to correct solve the mapping of IP / Host Name as before
  • Enable mod_remoteip in all Apache instances to solve the correct REMOTE_ADDR of the visitor because without this the REMOTE_ADDR will be instead the IP of HAProxy which is wrong
  • Block the ports 12080 and 12443 in your firewall, in all instances, to be accessible only by all the HAProxy inatances in the cluster, from all IP addresses that they will serve
  • Configure HAProxy frontend, in each instances, to listen to the port 80 and 443, serve HTTP1.1, HTTP2 and HTTP3
  • Configure HAProxy backend, in each instances, to serve from any Apache servers in the cluster that run on ports 12080 and 12443 using a load balancing algorithm such as round-robin by example
  • Set in all HAProxy instances a reasonable rate limit policy to protect against DDOS attacks

(c) 2022 w3soft.org, license: GPLv3 learning resources for software development and operating systems administration configuration HAProxy as a proxy or load balancer with Apache Server(s) haproxy conf
Sample HAproxy configuration as a simple proxy or load balancer between Apache and clients

# HAProxy sample config: as proxy with Apache

defaults
    log               global
    mode              http
    timeout           connect          5s
    timeout           client          50s
    timeout           server          50s
    timeout           queue           50s
    timeout           http-request    10s  # Preventing Slowloris like attacks
    timeout           http-keep-alive 10s
    timeout           check           10s
    option            httplog         clf
    option            dontlognull
    option            redispatch
    option            http-server-close
    retries            3
    maxconn           50
    unique-id-format  %{+X}o\ %ci:%cp_%fi:%fp_%Ts_%rt:%pid
    unique-id-header  X-Unique-ID

frontend static-local-haproxy
    bind             *:80
    mode             http
    log              global
    option           httpclose
    option           forwardfor
    http-request     redirect location https://%[req.hdr(Host),field(1,:)]:443%[capture.req.uri] unless { ssl_fc }
    default_backend  static-apache
    # use a rate limit ; for supporting HTTP1.1 which makes many connection per request you may wish to disable rate limit for static resources
    http-request     track-sc0 src table static-apache unless { path_end .css .ttf .woff2 .js .svg .webp .webm .png .jpg .gif .pdf .txt }
    # for use with HTTP2 / HTTP3 only (no HTTP 1.1) uncomment the next line and comment the above line
#   http-request     track-sc0 src table static-apache
    http-request     deny deny_status 429 if { sc_http_req_rate(0) gt  30 }

frontend static-local-haproxy-ssl
    bind             *:443      ssl crt /etc/apache2/cert.pem alpn h2,http/1.1
    # to enable HTTP3 (since HAProxy 2.6, see an example on the line below ; the above line only enables HTTP2 and HTTP1.1 as a fallback ; you can combine all 3 protocols)
#   bind             quic@*:443 ssl crt /etc/apache2/cert.pem alpn h3
    mode             http
    log              global
    option           httpclose
    option           forwardfor
    http-request     redirect scheme https unless { ssl_fc }
    default_backend  static-apache
    # use a rate limit ; for supporting HTTP1.1 which makes many connection per request you may wish to disable rate limit for static resources
    http-request     track-sc0 src table static-apache unless { path_end .css .ttf .woff2 .js .svg .webp .webm .png .jpg .gif .pdf .txt }
    # for use with HTTP2 / HTTP3 only (no HTTP 1.1) uncomment the next line and comment the above line
#   http-request     track-sc0 src table static-apache
    http-request     deny deny_status 429 if { sc_http_req_rate(0) gt  30 }

backend static-apache
    mode          http
##  use set-header NOT add-header ; set-header will rewrite any existing value and is safe against spoofing !
    http-request  set-header X-Forwarded-Client-Ip %[src]
    http-request  set-header X-Forwarded-Domain %[req.hdr(Host),field(1,:),lower]
    http-request  set-header X-Forwarded-Ip %[dst]
    http-request  set-header X-Forwarded-Port %[dst_port]
    http-request  set-header X-Forwarded-Proto http  if !{ ssl_fc }
    http-request  set-header X-Forwarded-Proto https if  { ssl_fc }
    http-request  set-header X-Forwarded-Https off   if !{ ssl_fc }
    http-request  set-header X-Forwarded-Https on    if  { ssl_fc }
    server        server1 127.0.0.1:12080 check
    # to connect to apache via SSL port instead of plain port uncomment the next line and comment the above line (for local IP or when ports are firewalled makes non-sense to connect to apache via SSL)
#   server        server1 127.0.0.1:12443 check ssl verify none
    stick-table   type ip   size 5k  expire 30s  store http_req_rate(10s)
#   stick-table   type ipv6 size 5k  expire 30s  store http_req_rate(10s)



# HAProxy sample config: as load balancer with Apache

# change in the above config this section, as follows (don't forget to adapt your IP addresses from below)
backend static-apache
    mode          http
##  use set-header NOT add-header ; set-header will rewrite any existing value and is safe against spoofing !
    http-request  set-header X-Forwarded-Client-Ip %[src]
    http-request  set-header X-Forwarded-Domain %[req.hdr(Host),field(1,:),lower]
    http-request  set-header X-Forwarded-Ip %[dst]
    http-request  set-header X-Forwarded-Port %[dst_port]
    http-request  set-header X-Forwarded-Proto http  if !{ ssl_fc }
    http-request  set-header X-Forwarded-Proto https if  { ssl_fc }
    http-request  set-header X-Forwarded-Https off   if !{ ssl_fc }
    http-request  set-header X-Forwarded-Https on    if  { ssl_fc }
    balance       roundrobin
    server        server1 169.254.12.100:12080 check
    server        server2 169.254.28.150:12080 check
    server        server3 169.254.78.120:12080 check