Announcing HAProxy 2.6
Version Update

HAProxy 3.1 is now the latest version. Learn more

HAProxy 2.6 is now available!

As always, the community behind HAProxy made it possible to bring the enhancements in this release. Whether developing new functionality, fixing issues, writing documentation, QA testing, hosting CI environments, or submitting bug reports, members of our community continue to drive the project forward. If you’d like to join the effort, you can find us on GitHubSlackDiscourse, and the HAProxy mailing list.

Register for the webinar HAProxy 2.6 Features Roundup to learn more about this release and participate in a live Q&A with our experts.

In the following sections, you will find a list of changes included in this version.

HTTP/3 over QUIC

This version of HAProxy adds experimental support for HTTP/3 over QUIC, which is a novel approach to transmitting HTTP messages over UDP instead of TCP. The benefits include fewer round trips between the client and server when establishing a TLS connection, better protection against denial-of-service attacks, and improved connection migration when the user switches between networks.

In the example configuration below, we enable HTTP/3 over QUIC by setting a bind line that listens for client connections on UDP port 443. The prefix quic4@ sets the protocol. Also note that we return an alt-svc HTTP header, which instructs the client’s browser to switch to the new protocol for subsequent requests. In other words, the first request will be HTTP/2, but any after that will be HTTP/3.

frontend mysite
bind :80
bind :443 ssl crt /etc/haproxy/certs/foo.com/cert.pem alpn h2
# enables HTTP/3 over QUIC
bind quic4@:443 ssl crt /etc/haproxy/certs/foo.com/cert.pem alpn h3
# Redirects to HTTPS
http-request redirect scheme https unless { ssl_fc }
# 'Alt-Svc' header invites client to switch to the QUIC protocol
# Max age (ma) is set to 15 minutes (900 seconds), but
# can be increased once verified working as expected
http-response set-header alt-svc "h3=\":443\";ma=900;"
default_backend webservers

Something else to know is that HAProxy supports stateless reset packets with QUIC, but you must set the global directive cluster-secret, which HAProxy uses to derive a stateless reset token. The token protects against malicious actors sending spoofed reset packets.

You’ll need to compile HAProxy with a few new options, including the USE_QUIC flag, and also link to a QUIC-compatible version of OpenSSL, such as the one found here. Want to try this out? Check out our HTTP/3 demo project.

Generic Hash Load Balancing Algorithm

You can use the new load balancing algorithm, hash, in place of the existing, more specific hash algorithms sourceurihdrurl_param, and rdp-cookie. The new algorithm is generic, thus allowing you to pass in a sample fetch of the data used to calculate the hash.

In the example below, the pathq fetch returns the URL path and query string for the data to hash:

backend cache_servers
balance hash pathq
hash-type consistent
server cache1 192.168.56.30:80 check maxconn 30
server cache2 192.168.56.31:80 check maxconn 30

SSL and TLS

You can compile HAProxy against OpenSSL 3.0, the latest branch of the OpenSSL library.

Authentication

To authenticate clients with client certificates, you set the ca-file parameter on your bind line to indicate which certificate authority (CA) to use to verify the certificate. This parameter now accepts a directory path, allowing you to load multiple CA files so that you can verify certificates that were signed by different authorities.

frontend www
bind :443 ssl crt /etc/haproxy/certs/site.pem verify required ca-file /etc/haproxy/ca/

Similarly, the ca-file parameter on a server line in a backend now accepts a directory path, allowing you load multiple CAs to verify a server’s SSL certificate. In this case, you can also specify @system-ca to load your system’s list of trusted CAs.

backend webservers
server s1 192.168.50.30:80 ssl ca-file @system-ca

Runtime API

A new Runtime API command, show ssl providers, available when HAProxy was compiled against OpenSSL 3.0, returns a list of providers loaded into OpenSSL. A provider implements cryptographic algorithms. You can load other providers via the OpenSSL configuration file, which you can find the path for by running openssl version -d.

$ echo "show ssl providers" |\
sudo socat stdio /var/run/haproxy/api.sock
Loaded providers :
- default

Next, the Runtime API’s dynamic server feature, which was introduced in HAProxy 2.4 and got expanded keyword support in HAProxy 2.5, is no longer experimental. Recall that the dynamic server functions let you create servers on the fly without reloading the HAProxy configuration.

Also, you can now set the check and check-ssl parameters when creating servers, which were unsupported in prior versions. Note that when enabling health checks with these parameters, HAProxy is not yet able to implicitly inherit the SSL or Proxy Protocol configuration of the server line, so you must explicitly use check-ssl and check-send-proxy, even if the health check port is not overridden.

Master CLI

The Master CLI provides an interface for working with the HAProxy worker processes. You can learn more about it in the blog post Get to Know the HAProxy Process Manager. The CLI received several new commands:

Command

Description

prompt

Begins an interactive session with the CLI.

expert-mode [on|off]

Activates expert mode for every worker accessed from the Master CLI.

experimental-mode [on|off]

Activates experimental mode for every worker accessed from

the Master CLI.

mcli-debug-mode [on|off]

Allows a special mode in the Master CLI which enables all

keywords that were meant for a worker on the Master CLI, allowing you to debug the master process. Once activated, you list the new available keywords with “help”. Combined with “experimental-mode” or “expert-mode” it enables even

more keywords.

The starting point is the prompt command, which starts an interactive session. Once in a session, you can enable expert, experimental, and master CLI debug modes. Then, send Runtime API commands to one of the worker processes. Some Runtime API commands become available only in one of the aforementioned modes.

Here is an example:

$ sudo socat /run/haproxy-master.sock -
prompt
master> expert-mode on
master(e)> master(e)> show proc
#<PID> <type> <reloads> <uptime> <version>
5772 master 0 [failed: 0] 0d00h44m58s 2.6-dev11-d8c195-40
# workers
5798 worker 0 0d00h44m58s 2.6-dev11-d8c195-40
master> @!5798 help
The following commands are valid at this level:
abort ssl ca-file <cafile> : abort a transaction for a CA file
abort ssl cert <certfile> : abort a transaction for a certificate file
abort ssl crl-file <crlfile> : abort a transaction for a CRL file
add acl [@<ver>] <acl> <pattern> : add an acl entry
...

Lua

When extending HAProxy with a custom Lua module, you can now update an SSL certificate in the memory of the current HAProxy process by using the CertCache class. In the snippet below, the certificate and key are hardcoded in the Lua file, but in practice you could fetch these using the HTTP client or receive them from HAProxy variables, for example.

local mycertificate=[[-----BEGIN CERTIFICATE-----
MIIDKDCCAhCgAwIBAgIIIbG+G46oc6cwDQYJKoZ...]]
local mykey=[[-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAlB0IloGiMuIJblHBsJ1wQ2zY...]]
CertCache.set{filename="certs/cert.pem", crt=mycertificate, key=mykey}

Also, the HTTP client that was added in version 2.5, which lets you make non-blocking HTTP calls from Lua, now supports two new parameters: dst for setting the destination address and timeout for setting a timeout server value. Setting dst overrides the IP address and port in the url parameter, but keeps the path. Below, the destination URL becomes http://127.0.1.1:8001/test.

local httpclient = core.httpclient()
local response = httpclient:get{
url="http://127.0.0.1:8000/test",
dst="127.0.1.1:8001",
timeout=10s}

However, since it supports HAProxy bind-style addresses, a more interesting use case is to set dst to a UNIX socket. For example, you could query the Docker API, which listens at the UNIX socket /var/run/docker.sock, to fetch a JSON-formatted list of running containers from within your Lua code, as shown in the next snippet:

local httpclient = core.httpclient()
local response = httpclient:get{
url="http://v2/containers/json",
dst="unix@/var/run/docker.sock"}
-- response.body is the string:
-- [{"Id":"d7bed8420b56dff2f...","Names":["/happy_wright"],"Image":"jmalloc/echo-server",...
-- write response to HAProxy log
core.Debug(response.body)

This is functionally equivalent to calling the Docker API with curl:

$ sudo curl -X GET -s \
--unix-socket /var/run/docker.sock \
http://v2/containers/json

Furthermore, new global directives in the HAProxy configuration affect the httpclient class:

Global directive

Description

httpclient.ssl.ca-file <cafile>

This option defines the ca-file which should be used to verify the server certificate. It takes the same parameters as the ca-file option on the server line.

By default and when this option is not used, the value is “@system-ca” which tries to load the CA of the system. If it fails the SSL will be disabled for the httpclient.

However, when this option is explicitly enabled it will trigger a configuration error if it fails.

httpclient.ssl.verify [none|required

Works the same way as the verify option on server lines. If set to ‘none’, server certificates are not verified. Default option is “required”.

By default and when this option is not used, the value is “required”. If it fails the SSL will be disabled for the httpclient.

However, when this option is explicitly enabled it will trigger a configuration error if it fails.

httpclient.resolvers.id <resolvers id>

This option defines the resolvers section with which the httpclient will try to resolve.

Default option is the “default” resolvers ID. By default, if this option is not used, it will simply disable the resolving if the section is not found.

However, when this option is explicitly enabled it will trigger a configuration error if it fails to load.

httpclient.resolvers.prefer <ipv4|ipv6>

This option allows you to chose which family of IP addresses you want when resolving, which is convenient when IPv6 is not available on your network. Default option is “ipv6”.

Listing Configuration Keywords

Have you ever wanted to know whether a configuration keyword is supported in the version of HAProxy you’re running? You can now ask HAProxy to return to you a list. Keywords are sorted into classes, so first get the list of classes by passing the -dKhelp argument to HAProxy, along with the quiet (-q), validation check (-c) and configuration file (-f) arguments:

$ sudo haproxy -dKhelp -q -c -f /dev/null
# List of supported keyword classes:
all: list all keywords
acl: ACL keywords
cfg: configuration keywords
cli: CLI keywords
cnv: sample converter keywords
flt: filter names
smp: sample fetch functions
svc: service names

Then get a list of keywords, for example:

$ sudo haproxy -dKacl -q -c -f /dev/null
# List of registered ACL keywords:
base = base -m str
base_beg = base -m beg
base_dir = base -m dir
base_dom = base -m dom
base_end = base -m end
base_len = base -m len
base_reg = base -m reg
base_sub = base -m sub
[...]

Protocol Updates

A new global directive, h1-accept-payload-with-any-method, allows clients using HTTP/1.0 to send a payload with GET, HEAD, and DELETE requests. The HTTP/1.0 specification had not been clear on how to handle payloads with these types of requests and proxy implementations vary on the interpretation, which could lead to request smuggling attacks. HAProxy uniformly rejects these requests for that reason, but the new option allows you to turn off this safeguard if you need to support specific clients.

Seamless Reloads

Since HAProxy 1.8, HAProxy has had seamless reloads, which means you can use systemctl reload haproxy to update the HAProxy configuration without dropping any active connections, even under very high utilization. Listening sockets transfer over to the new worker process during the reload. The only thing you had to do was make sure that master-worker mode was enabled by including the -W flag when starting HAProxy and adding the parameter expose-fd listeners to a stats socket directive in the global section of your configuration:

global
stats socket /var/run/haproxy/api.sock mode 660 level admin expose-fd listeners

Now, you no longer need to do even that. Seamless reloads will work without any effort on your part. You can omit the expose-fd listeners parameter and the -W flag is already included in the Systemd service file in the HAProxy repository.

New Fetches and Converters

Two new fetches help pinpoint why a request was terminated. The last_rule_file fetch returns the name of the configuration file containing the final rule that was matched during stream analysis and the last_rule_line returns the line number. Add these to a custom log format to capture which rule in your configuration stopped the request.

In the next example, a custom log format includes these new fetches:

global
setenv HTTP_LOG "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
frontend mysite
...
log-format "${HTTP_LOG} %[last_rule_file] %[last_rule_line]"
# reject request here
# log will show: /etc/haproxy/haproxy.cfg 27
http-request deny if TRUE

Next, the new add_item converter concatenates fields and returns a string. The advantage this has over the existing concat converter is that it will place a delimiter, such as a semicolon, between fields, and check whether the field exists to avoid appending a trailing delimiter at the end of the string.

In the example below, the add_item converter sets an HTTP cookie with the Expires and Secure attributes, which are separated by semicolons.

frontend mysite
...
http-response set-var(res.cookie_name) str("foo")
http-response set-var(res.cookie_value) str("bar")
http-response set-var(res.expiration) date(3600,"s"),http_date
http-response set-var(res.cookie_expiration) str("Expires"),add_item("=",res.expiration)
http-response set-var(res.cookie_secure) str("Secure")
http-response add-header Set-Cookie %[var(res.cookie_name),add_item("=",res.cookie_value),add_item(";",res.cookie_expiration),add_item(";",res.cookie_secure)]
# Produces the header:
# set-cookie: foo=bar;Expires=Fri, 27 May 2022 03:13:58 GMT;Secure

Variables

Variables let you store information about a request or response and reference that information within logical statements elsewhere in your configuration file. HAProxy 2.6 makes it simple to check whether a variable already exists or already has a value before trying to set it again. All tcp- and http- set-var actions, such as http-request set-var and tcp-request content set-var, now support the new parameter.

For example, if you wanted to set a variable named token to the value of an HTTP header named X-Token, but fall back to setting it to the value of a URL parameter named token if the header doesn’t exist, you could use the condition isnotset to check whether the variable has a value from the first case before trying to set it again:

frontend mywebsite
bind :80
# try using the value from the X-Token header
http-request set-var(txn.token) req.hdr(X-Token)
# fall back to using the value from the URL parameter 'token'
http-request set-var(txn.token,ifnotset) url_param(token)
# log the variable
http-request capture var(tnx.token) len 10
default_backend webservers

You can use the following, built-in conditions:

Condition

Sets the new value when…

ifexists

the variable was previously created with a set-var call.

ifnotexists

the variable has not been created yet with a set-var call.

ifempty

the current value is empty. This applies for nonscalar types (strings, binary data).

ifnotempty

the current value is not empty. This applies for nonscalar types (strings, binary data).

ifset

the variable has been set and unset-var has not been called. A variable that does not exist is also considered unset.

ifnotset

the variable has not been set or unset-var was called.

ifgt

the variable’s existing value is greater than the new value.

iflt

the variable’s existing value is less than the new value.

Performance Tuning

HAProxy 2.6 brings new way to improve load balancing performance:

  • Adding fd-hard-limit to the global section of your configuration will enforce a cap on the number of file descriptors that HAProxy will use, even when the system allows many more, which protects you from consuming too much memory. If you set a global maxconn setting higher than this, the maxconn will adapt to this hard limit. Learn about setting maximum connections.

  • The new global directive close-spread-time lets you close idle connections gradually over a period of time, rather than all at once, which had caused reconnecting clients to rush against the process. For best results, you should set this lower than the hard-stop-after directive.

  • HAProxy’s task scheduler code and the code that dequeues connections awaiting an available server got a performance boost. The code, which uses multithreading, was optimized to bypass thread locking, allowing server queue management to become much more scalable.

  • The connection stream code has been refactored to simplify it and reduce the number of layers. Although more work in this area is underway, the result will be a more linear architecture, resulting in fewer bugs and easier maintenance.

  • At startup, HAProxy inspects the CPU topology of the machine and if a multi-socket machine is detected, sets an affinity in order to run on the CPUs of a single node in order to not suffer from the performance penalties caused by the inter-socket bus latency. However, if this causes inferior performance, you can set the no numa-cpu-mapping directive.

Contributors

We would like to thank each and every contributor who was involved in this release. Contributors help in various forms such as discussing design choices, testing development releases, reporting detailed bugs, helping users on Discourse and the mailing list, managing issue trackers and CI, classifying Coverity reports, maintaining the documentation, operating some of the infrastructure components used by the project, reviewing patches, and contributing code.

Subscribe to our blog. Get the latest release updates, tutorials, and deep-dives from HAProxy experts.