Hi,
HAProxy 3.5-dev6 was released on 2026/09/03. It added 90 new commits
after version 3.5-dev5.
Roughly 35 bugs were fixed all over the code from what I can see.
For the main changes:
- decompression, on request and response. The main goal is to allow to
compress both requests and responses between two haproxy nodes in
different AZ to cut bandwidth costs. It's even possible to decompress
and recompress using the algos supported by the client. For now it's
made during the forwarding stage (i.e. on output), which means that
a decompressed request cannot be inspected by a wait-body rule, if
that matters for some users. This is due to the limitations of the
filters architecture but we have ideas to overcome that, and at the
moment this is a really tiny limitation (and if one would absolutely
need to analyze decompressed contents, they can always chain two
layers). This is only supported with USE_SLZ for now, because the
implementation benefits from facilities allowing it to be significantly
faster than zlib on streams compressed by slz (but it can of course
decompress regular gzip/deflate). Interestingly, the compression
performance even allows to reduce latency for not-that-big objects
(typically 10->9ms for 10kB objects over GigE link). Please have a
look at the "decompression" keyword if you're interested, and share
your findings. We're not aware of any remaining issue on this part
(no more truncated response etc).
- some hardening on H1 and H2 to avoid claiming an end of message on
detected truncation. The point is to avoid the risk of re-inserting a
reusable connection in an idle list if it has seen a partial message,
as this could lead to smuggling attacks in case it would be combined
with another bug that triggers it. Normally this should never trigger.
Normally... We may backport this to a few recent versions in the
future but it will have to wait for more exposure to be certain we
didn't overlook a valid corner case.
- resolvers: name servers designated only via an IP address and port
(i.e. no explicit transport) will now be used in UDP first, and
retried over TCP for truncated responses. This provides the best
combination of latency and large payload, and corresponds to what
modern resolvers tend to do. Having it automatic ensures that when
the resolver is taken from resolv.conf, it continues to work without
any modification and benefits from the new feature. Please give this
version a test if you're relying on resolvers, to make sure that
nothing was overlooked.
- per-thread-group FD: we also identified reverse-HTTP as incompatible
so a new test was added to reject configs involving both to let the
user choose.
- some locking hardening: a bug was found in the rings where initialization
was made at boot under thread isolation while the caller could hold a
lock, resulting in an occasional deadlock at moderate loads upon a
reload. This must never happen so a new check was added for this and a
panic will instantly be triggered if a code construct does this. While
auditing the code we didn't find another one, but if you get a message
about lock and thread isolation, it means we missed one.
- the SSL verify callback no longer falls back to Subject CN when a SAN
dNSName is present, as required by RFC6125 (previously it would do it
for any SAN but it's forbidden for those ones). In theory, no impact
for properly emitted certs.
- logs: the JSON encoder could copy contents verbatim to the output, but
the JSON spec says that JSON contents are exclusively UTF-8, while HTTP
headers are only opaque bytes (and often latin1), meaning that logging
such contents would produce invalid JSON. The doc section 8.6 about logs
says that characters outside of 32..126 are encoded in hex as #HH, but
this wasn't done there. Now it is. If you happen to send non-printable
characters in your logs, you will see a difference, now the code does
what the doc says, and now the output is safe and unambiguous. Along
this, the "show events" CLI command adopted a new option, "-s" to
sanitize its output to protect the terminal from untrusted input (i.e.
characters outside of this range). It could make sense to have another
one to force it in raw mode (e.g. "-r"), and change the default after
a few versions once clients / dataplane API has adopted the new one.
This is left on the todo list. Ah, and the CBOR format encoder was
carefully reworked to factor frequent operations. It is now much
faster, with the overall request rate raising by 12% when using this
format (or same savings for CPU usage at the same req rate).
- haload got plenty of new features (percentiles, think time, probe
mode to automatically restart after a service is back online, choice
of HTTP methods, fcgi support, cookies, per-thread-group FD table).
- QUIC's stateless reset was not rate-limited, and we think that there
is no reason for sending millions of them per second if haproxy
receives millions of spoofed packets per second (rough equivalent
for TCP reset). So a rate limit was set, which defaults to 1000 per
second (should already be plenty), and that can be changed via
tune.quic.sec.stateless-reset-rate.
- it was found that adding many dynamic servers in a backend without
specifying an ID could take some time to find the next unused ID, in
the order of 3 milliseconds at 50000 servers in a backend, causing a
major slowdown to those using large farms. This is now addressed and
we're down to around 8 microseconds now regardless of the number of
servers.
The rest is cleanups (quite a bit, that's cool) and more regtests (nice
as well).
Features are interesting and a bit more sensitive. Please test carefully,
particularly if you're using some of the changed ones, and report both
successes and failures if any. As usual, it's already running on
haproxy.org. Thanks to all participants.
BTW, speaking about haproxy.org, we had a network outage on the secondary
server we've been running on over the last few months, forcing an
emergency switch back to the first one. We found that some files were not
updated (the tarballs and changelogs mainly since they're produced when we
release). Synchronizing them automatically is trickier than the git repos
and we need to find the best way to do it without ruining valid files on
reconnection. The two servers have now been resynchronized and we'll soon
switch back to the secondary again to make sure everything's OK. So if you
get a 404, please report it to me directly (no need to pollute the list
for such admin stuff).
Please find the usual URLs below :
Site index : https://www.haproxy.org/
Documentation : https://docs.haproxy.org/
Wiki : https://github.com/haproxy/wiki/wiki
Discourse : https://discourse.haproxy.org/
Slack channel : https://slack.haproxy.org/
Issue tracker : https://github.com/haproxy/haproxy/issues
Q&A from devs : https://github.com/orgs/haproxy/discussions
Sources : https://www.haproxy.org/download/3.5/src/
Git repository : https://git.haproxy.org/git/haproxy.git/
Git Web browsing : https://git.haproxy.org/?p=haproxy.git
Changelog : https://www.haproxy.org/download/3.5/src/CHANGELOG
Dataplane API :
https://github.com/haproxytech/dataplaneapi/releases/latest
OpenTelemetry : https://github.com/haproxytech/haproxy-opentelemetry
Pending bugs : https://www.haproxy.org/l/pending-bugs
Reviewed bugs : https://www.haproxy.org/l/reviewed-bugs
Code reports : https://www.haproxy.org/l/code-reports
Latest builds : https://www.haproxy.org/l/dev-packages
Willy
---
Complete changelog :
Amaury Denoyelle (1):
BUG/MAJOR: h3: reject H3 truncated frames
Aurelien DARRAGON (10):
OPTIM: log/tools: switch from function pointer design to dual mode
HEX/BIN design for CBOR encoding
IMPORT: slz/uslz: support SLZ_FMT_NONE format and force its value to 0
MINOR: buf: make b_rep_blk() wrapping-proof
MINOR: (de)compression: add decompression infrastructure
MEDIUM: flt-decomp: implement decomp-req and decomp-res filters
MINOR: flt-decomp: add support for HTTP decompression
DOC: config: add documentation for decompression filter
REG-TESTS: decompression: add basic decompression test
MINOR: flt-decomp: warn if built without USE_SLZ
MEDIUM: filters: implicitly enable "decomp-req" and "decomp-res" when
decompression options are used
Austin Kauffman (1):
BUG/MINOR: fcgi-app: allow explicit filter declaration with
non-cache/non-compression filters
Christopher Faulet (8):
BUG/MINOR: flt-http-comp: Don't read next block to detect end of data
BUG/MEDIUM: flt-http-comp: Block HTX_FL_EOM flag till the end of
compression
BUG/MEDIUM: mux-spop: Properly handle parsing of split HELLO/DISCONNECT
frames
MINOR: flt-http-comp: Check request headers for response compression
earlier
REGTESTS: decomp: Add more scripts to test HTTP/TCP decompression
BUILD: flt-decomp: Remove unused stream_get_decomp() function
BUN/MINOR: hlua: reset hlua_body variable after per-thread files loading
BUG/MEDIUM: resolvers: Use the right server to init DNS streams
Dragan Dosen (1):
BUG/MEDIUM: filters: check the filter name before comparing it
Frederic Lecaille (26):
MINOR: haload: add -get/-head/-post options to select the HTTP method
BUG/MINOR: haload: count timed-out requests as errors, not successes
BUG/MINOR: haload: make -e react to request timeouts too
MINOR: haload: add -ee to probe and ramp back up after an error
MINOR: quic: add tune.quic.sec.stateless-reset-rate
BUG/MINOR: haload: split the rate exactly across threads
BUG/MINOR: haload: -ee could burst past its floor when many users fail at
once
CLEANUP: haload: factor out the per-thread rate share computation
CLEANUP: haload: factor out two more duplicated -ee blocks
MINOR: haload: keep old-scheduled users under -ee's floor rate
MINOR: haload: add -T to add a think time after each response
MINOR: haload: always track and resend cookies per user
BUG/MINOR: haload: fix hld_new_usr()'s error path
MINOR: haload: add -P to report ttfb/ttlb percentiles
MINOR: haload: enable per-thread-group FD tables when available
BUG/MINOR: quic: fix zero value of tune.quic.sec.stateless-reset-rate
BUG/MEDIUM: quic: crash on key update phase change after a failed one
BUG/MINOR: quic: late packets of the first key phase are dropped
CLEANUP: quic: remove the unused packet number of the key phase structure
MINOR: haload: rename <hld_http_ver> to <hld_proto>
MINOR: mux-fcgi: also skip sc_opposite() for haload streams
MINOR: haload: add FastCGI support
MINOR: quic: protect the ChaCha20 header protection context from being
freed
BUG/MINOR: qpack: encoder and decoder stream data is never consumed
BUG/MINOR: qpack: accept a Required Insert Count which cannot be reached
BUG/MINOR: quic: delete the wrong CIDs when post-handshake frames fail
Manu Nicolas (8):
BUG/MINOR: resolvers: classify empty truncated responses
CLEANUP: resolvers: stop clearing the DNS response buffer
MINOR: tools: report explicit address type selection
CLEANUP: dns: use a single maximum DNS message size
MINOR: dns: select the nameserver server type explicitly
MEDIUM: resolvers: accept full-size DNS messages over streams
MEDIUM: resolvers: retry truncated UDP responses over TCP
REGTEST: resolvers: cover UDP to TCP fallback
Olivier Houchard (6):
BUG/MEDIUM: mux-h1: close the connection on a short content-length
MEDIUM: mux-h2: don't report EOM on a short content-length message
MEDIUM: servers: Do not allow reverse HTTP with per-tg fd tables
BUG/MEDIUM: server: Fix server-template with default-server
MEDIUM: mux-h1: Harden test for short data at end of stream
BUG/MEDIUM: mux-h2: Make sure we remove H2_SF_NOTIFIED on subscribe
Remi Tricot-Le Breton (2):
BUG/MINOR: cache: Manage collisions on primary key
BUG/MINOR: cache: Seed cache primary hash
William Lallemand (5):
BUILD: ssl: avoid a potential null-dereference warning on OpenSSL 1.0.2
BUG/MINOR: payload: fix the cipher_len bound check in
smp_client_hello_parse()
BUG/MINOR: payload: bound ClientHello extension lists by the extension
length
MEDIUM: ssl: do not fall back to Subject CN when a SAN dNSName is present
REGTESTS: ssl: add verifyhost SAN vs CN matching test
Willy Tarreau (21):
BUG/MEDIUM: connection: fix an infinite loop in the fc_pp_tlv() fetch
BUG/MEDIUM: sink: do not hold the sft lock around ring_dispatch_messages()
MEDIUM: threads: always abort when calling thread_isolated() under a lock
MEDIUM: ring: attach readers under the tail lock instead of isolating
BUG/MEDIUM: cache: ignore cache on redundant origin/referer
BUG/MEDIUM: sink: initialize the settings of the implicit log server
BUG/MEDIUM: sink: do not create one implicit ring per logger copy
CLEANUP: sink: initialize and finalize the implicit forward proxy
OPTIM: server: remember the lowest known unused server ID
REG-TESTS: decompression: check the response header rewriting
IMPORT: uslz: do not replay the header-detection bytes on the raw deflate
path
IMPORT: slz: get rid of the bogus USE_64BIT_QUEUE and check siezof(long)
instead
MINOR: ring: add "-s" flag to "show events" to sanitize the output
REGTESTS: log: add a test for "show events -s"
REGTESTS: log: check which log-format tags escape control characters
BUG/MINOR: tools: support the documented NULL in
cbor_encode_bytes_prefix_bin()
BUG/MEDIUM: log: encode the whole non-printable range in the +json output
REGTESTS: log: check the escaping performed by the +json option
DOC: config: mention that section 8.6 does not apply to the cbor encoding
REGTESTS: log: check that the +cbor output passes the logged bytes through
CLEANUP: h2: remove dead code around FND_AUTH in h2_prepare_htx_reqline()
Yongqiang Tian (1):
BUG/MINOR: debug: restore complain tail jumps with clang
---