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.
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
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