Useful HAProxy Tips and Tricks – Part 1
Overview:
This guide explains some key tips and tricks for deploying and configuring HAProxy as a load balancer and reverse proxy in a production environment.
Page Contents
Preface
HAProxy offers several basic, standard, and advanced features for HTTP reverse proxying, TCP proxying, and load balancing with SSL termination. These features can be enabled using various categorized configuration directives and parameters. These directives may be added in one or more of the four essential sections of an HAProxy configuration.
The four sections are:
- global – which contains settings that
- defaults – which contains settings that apply to all of the
frontend
andbackend
sections that come after it, - frontend – this section defines the IP addresses and ports that clients can connect to, usually the * symbol is used to define all IP addresses of the server where HAProxy is installed, and
- backend – this section defines a group of upstream servers that will be load-balanced and assigned to handle requests.
Another section is the listen section. It can be used to combine the functionality of a frontend and a backend into one. However, to make the configuration file more readable, most users separate the frontend and backend sections.
Importantly, not all directives can work in all sections. Some directives work in specific sections. The HAProxy documentation (link under references) clearly defines all sections in which each directive is valid.
Okay, let’s get started with the tips and tricks.
1. Define Common Settings in the Defaults Section
To avoid repeating settings in many sections, if a setting you have added is supposed to apply to all other frontend and backend sections, it is better you add it in a default section. Note that you can have many default sections. Subsequent defaults
sections will override those that came before and reset all options to their default values.
2. Set the Correct HAProxy Working Mode
By default, HAProxy can operate in two modes: HTTP mode and TCP mode. You need to know when to use what mode when deploying HAProxy.
For more information, read: When To Run HAProxy in HTTP or TCP Modes
3. Enable SSL Termination
HAProxy only supports PEM-formatted SSL certificates which should contain all necessary SSL files including the public certificate and the private key. Once you have your certificate file and the key, you can create a .prm file like this:
#cat example1.com.key example1.com.pem >/etc/ssl/certs/example1.com.pem
To enable SSL termination, add the ssl
parameter to the bind directive listener. And use the crt
parameter to identify the location of the SSL certificates. You can add multiple crt parameters to enable multiple SSL certificates as shown.
frontend http_in bind *:80 bind *:443 ssl crt /etc/ssl/certs/example1.com.pem crt /etc/ssl/certs/example2.com.pem crt /etc/ssl/certs/example3.com.pem
4. Limit supported SSL versions
HAProxy allows for limiting supported versions of SSL to be negotiated. This allows you to disable usage of SSL versions that are known to have vulnerabilities in them. Simply use the ssl-default-bind-options ssl-min-ver setting in the global section as shown. Possible values are SSLv3, TLSv1.0, TLSv1.1, TLSv1.2, and TLSv1.3.
In this example, I have set the minimum accepted SSL version to TLSv1.2, meaning versions below it TLSv1.1, TLSv1.0, and SSLv3, will not be negotiated.
global ssl-default-bind-options ssl-min-ver TLSv1.2
5. Redirect HTTP to HTTPS
To redirect HTTP to HTTPS, use the redirect scheme directive as shown:
frontend http_in bind *:80 bind *:443 ssl crt /etc/ssl/certs/example1.com.pem crt /etc/ssl/certs/example2.com.pem crt /etc/ssl/certs/example3.com.pem alpn h2,http/1.1 redirect scheme https code 301 if !{ ssl_fc }
6. Redirect www to non-www
To redirect www to non-www, add the redirect prefix directive as follows. Replace example.com with your domain:
redirect prefix https://example.com code 301 if { hdr(host) -i www.example.com }
7. Use a Specific Backend for a Domain
To use a specific backend for all requests to a specific domain name, use the use_backend directive as follows. Replace example1.com and example2.com with your domains:
use_backend example1_backend if { hdr(host) -m dom example1.com } use_backend example2_backend if { hdr(host) -m dom example2.com }
8. Set Load Balancing Algorithm
If you have several upstream servers defined in a backend or listen section, you can set the load balancing algorithm that HAProxy should use, using the balance directive. The default algorithm is roundrobin and possible values are leastconn, first, static-rr, hash, source, uri, url_param, hdr, random, and rdp-cookie.
This setting can only be applied in the default, listen, and backend sections.
balance leastconn
9. Delete HTTP Header in Response
To remove an HTTP header from a response, use the http-response del-header directive like this:
http-response del-header Server http-response del-header Via
10. Add or Set HTTP Header in Response
To add an HTTP header to a response, use the http-response add-header directive like this:
http-response add-header Server “FOSSGuides Load Balancer”
To set an HTTP header to a response, use the http-response set-header directive. It works just like the http-response add-header directive except that the header name is first removed if it exists. This example shows how to add HTTP security headers in HAProxy.
http-response set-header X-Frame-Options DENY http-response set-header X-XSS-Protection 1;mode=block http-response set-header X-Content-Type-Options nosniff http-response set-header Strict-Transport-Security max-age=31536000;includeSubDomains;preload http-response set-header Referrer-Policy no-referrer-when-downgrade http-response set-header Content-Security-Policy "default-src self *.example1.com example2.com fonts.googleapis.com fonts.gstatic.com" http-response set-header Expect-CT enforce,\ max-age=86400 http-response set-header Referrer-Policy strict-origin-when-cross-origin http-response add-header X-Permitted-Cross-Domain-Policies none http-response set-header Permissions-Policy "accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), xr-spatial-tracking=(), clipboard-read=(self), clipboard-write=(self), gamepad=()"
11. Set a Backen Server Cookie
To enable cookie-based persistence in a backend, use the cookie directive. In this example, the cookie name is SVR_ID (remember to set your custom cookie name) and the arguments are insert, indirect, nocache, dynamic, secure, and httponly.
cookie SVR_ID insert indirect nocache dynamic secure httponly dynamic-cookie-key KSNDKNklsndkn89jnwedn
12. Define Maximum Number of Connections
To set the maximum number of connections use the maxconn directive. If defined in the global section, it defines the maximum per-process number of concurrent connections.
global maxconn 80000
Besides, you can define a maxconn setting on each server line in a backend or listen section of your configuration. This allows HAProxy to limit the number of concurrent connections that it will relay to the upstream server.
backend webservers server nginx_svr1 10.10.1.5:80 maxconn 100 server nginx_svr1 10.10.1.6:80 maxconn 100 server nginx_svr1 10.10.1.7:80 maxconn 100
13. Enable Health Checks
If you are running many upstream servers, it’s recommended to enable health checks so that if one or even several servers fail, clients can still use your application as long as other servers are still running. HAProxy will perform health checks to know which servers are alive and ready to receive requests. HAProxy supports active and passive health checks.
Below are several examples of how to enable active health checks. This configuration snippet shows the simple form of active health checks in HAProxy. Here, HAProxy will try to establish a TCP connection every two seconds (the default inter parameter). After three failed connections (default fall parameter), the server is removed temporarily, until HAProxy gets at least two successful connections (default rise parameter), reinstating the server into the backend.
backend webservers server nginx_svr1 10.10.1.5:80 maxconn 100 check server nginx_svr1 10.10.1.6:80 maxconn 100 check server nginx_svr1 10.10.1.7:80 maxconn 100 check
You can customize the health check time intervals using the inter parameter (changes the interval between checks), the fall parameter (number of failed checks allowed after which server is marked down), and the rise parameter (sets how many passing checks there must be before the server is marked alive again).
backend webservers server nginx_svr1 10.10.1.5:80 maxconn 100 check inter 10s fall 5 rise 5 server nginx_svr1 10.10.1.6:80 maxconn 100 check inter 10s fall 5 rise 5 server nginx_svr1 10.10.1.7:80 maxconn 100 check inter 10s fall 5 rise 5
Alternatively, for web applications, you can use HTTP health checks using the option httpck directive like this:
backend webservers option httpchk server nginx_svr1 10.10.1.5:80 maxconn 100 check server nginx_svr1 10.10.1.6:80 maxconn 100 check server nginx_svr1 10.10.1.7:80 maxconn 100 check
To instruct HAProxy to expect a certain status code, you can use the http-check expect directive as shown:
backend webservers option httpchk http-check send meth GET uri /health http-check expect status 200 server nginx_svr1 10.10.1.5:80 maxconn 100 check server nginx_svr1 10.10.1.6:80 maxconn 100 check server nginx_svr1 10.10.1.7:80 maxconn 100 check
You can enable passive health checks like this.
backend webservers option httpchk http-check send meth GET uri /health http-check expect status 200 server nginx_svr1 10.10.1.5:80 maxconn 100 check observe layer7 error-limit 50 on-error mark-down server nginx_svr1 10.10.1.6:80 maxconn 100 check observe layer7 error-limit 50 on-error mark-down server nginx_svr1 10.10.1.7:80 maxconn 100 check observe layer7 error-limit 50 on-error mark-down
14. Fix Timeout Errors
To fix timeout errors in HAProxy, you need to adjust the timeout parameters. One of the most common errors is 504 gateway timeout. To understand more about the timeout directives and how to fix the 504 gateway timeout error, read this guide.
15. Always Check HAProxy Configuration for Syntax Correctness After Making Changes
Always remember to check your HAProxy configuration to ensure that it is valid (and the syntax is correct). This helps to ensure that a change to your configuration file does not unintentionally stop your load balancer and bring down your services.
To check your HAProxy configuration, run the following haproxy command where the -c flag implies check mode where the haproxy command checks the config file(s) and exits. The -f flag specifies the config file to be checked.
$sudo haproxy -c -f /etc/haproxy/haproxy.cfg OR #haproxy -c -f /etc/haproxy/haproxy.cfg
Sample HAProxy Configuration File
Sample HAProxy configuration file showing all the above tips and tricks defined:
global maxconn 100000 log 127.0.0.1 local2 chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock user haproxy group haproxy mode 660 level admin expose-fd listeners stats timeout 30s user haproxy group haproxy daemon ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 ssl-default-bind-options ssl-min-ver TLSv1.2 ssl-dh-param-file /etc/ssl/dhparam defaults log global mode http option httplog option dontlognull option forwardfor timeout connect 30s timeout check 30s timeout client 20m timeout server 30m 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 listen stats bind *:8500 stats enable stats hide-version stats uri /monitor stats realm Haproxy\ Statistics stats refresh 5s stats admin if TRUE stats auth admin:f9394tp@1ss frontend rq_receiver mode http bind *:80 bind *:443 ssl crt /etc/ssl/certs/example1.com.pem crt /etc/ssl/certs/example2.com.pem crt /etc/ssl/certs/example3.com.pem alpn h2,http/1.1 redirect scheme https code 301 if !{ ssl_fc } redirect prefix https://example.com code 301 if { hdr(host) -i www.example.com } use_backend example1_backend if { hdr(host) -m dom example1.com } use_backend example2_backend if { hdr(host) -m dom example2.com } # HSTS (63072000 seconds) http-response set-header Strict-Transport-Security max-age=63072000 default_backend webservers backend webservers balance roundrobin option httpchk GET /health http-check expect status 200 http-response del-header Server http-response del-header Via http-response del-header X-Kong-Proxy-Latency http-response del-header X-Kong-Upstream-Latency http-response add-header Server "Entryxit Server" http-response set-header X-Frame-Options DENY http-response set-header X-XSS-Protection 1;mode=block http-response set-header X-Content-Type-Options nosniff http-response set-header Strict-Transport-Security max-age=31536000;includeSubDomains;preload http-response set-header Referrer-Policy no-referrer-when-downgrade http-response set-header Expect-CT enforce,\ max-age=86400 http-response set-header Referrer-Policy strict-origin-when-cross-origin http-response add-header X-Permitted-Cross-Domain-Policies none http-response set-header Permissions-Policy "accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), xr-spatial-tracking=(), clipboard-read=(self), clipboard-write=(self), gamepad=()" cookie SVR_ID insert indirect nocache dynamic secure httponly dynamic-cookie-key kldf03434JKSNDVS server nginx_svr1 10.30.1.10:8080 check maxconn 500 check inter 10s fall 5 rise 5 server nginx_svr2 10.30.1.11:8080 check maxconn 500 check inter 10s fall 5 rise 5 server nginx_svr3 10.30.1.12:8080 check maxconn 500 check inter 10s fall 5 rise 5 server nginx_svr4 10.30.1.13:8080 check maxconn 500 check inter 10s fall 5 rise 5 backend example1_backend option httpchk HEAD / http-response del-header Server http-response del-header Via cookie SVR_ID insert indirect nocache dynamic secure httponly dynamic-cookie-key JKSBJD934903jksndksd server svr1 10.10.3.60:80 check check inter 10s fall 5 rise 5 backend example2_backend balance leastconn http-response del-header Server http-response del-header Via cookie SVR_ID insert indirect nocache dynamic secure httponly dynamic-cookie-key KLSND93lsdkldkl30940934 server svr1 10.10.2.69:80 check check inter 10s fall 5 rise 5 server svr2 10.10.2.69:80 check check inter 10s fall 5 rise 5
Final words
That is all I have prepared for you in this guide. In upcoming guides, I will be expounding on many of these tips and tricks to show more advanced configuration options. I would like to hear from you. If you have any tips or tricks to add here or any comments to share, use the feedback form below.
References
1. https://www.haproxy.com/blog/the-four-essential-sections-of-an-haproxy-configuration
2. https://www.haproxy.com/blog/haproxy-ssl-termination
3. https://www.haproxy.com/blog/how-to-enable-health-checks-in-haproxy
4. https://docs.haproxy.org/