Hi, HAProxy 3.4.1 was released on 2026/06/25. It added 43 new commits after version 3.4.0.
This first maintenance release addresses a number of bugs accumulated since 3.4.0 was released. The most notable fixes concern external health checks, QUIC and HTTP/3, the HTX data draining in several multiplexers, and some SSL/TLS regressions. Several bugs were found and fixed in the external health check feature. A NULL-pointer dereference could occur during the tcpcheck post-config phase when an external check was configured on a backend. The fix ensures external checks bypass this step entirely. Related to this, the "small-buffer" option was not guarded against external checks either, resulting in another crash when enabled with external checks. These two issues are specific to 3.4. In addition, when a server configured to use tune.max-checks-per-thread was deleted while its check was still queued, a use-after-free would occur; the fix dequeues the check before freeing the server. The "healthcheck" keyword on default-server lines was parsed but never propagated to actual servers; it is now properly applied. Finally, a server-level healthcheck section was ignored when an external check was also configured at the proxy level; the server-level healthcheck now correctly takes precedence. Multiple bugs were fixed in the QUIC multiplexer. A potential infinite loop was possible in qcc_io_recv() when a RESET_STREAM was received while the stream was still in the recv_list; the loop is now correctly guarded. A related issue where the recv loop was unnecessarily interrupted on stream-level errors or incomplete data was also fixed. A transfer freeze could occur after a QCS rxbuf realign operation because the next Rx buffer was not reinserted into the QCS tree after the data move; this caused data loss particularly on POST requests on the frontend or on most backend transfers. Another issue in qcs_http_reset_buf() caused the entire buffer to be reset unconditionally instead of draining only the requested amount, which could discard unsent data when a filter was registered on the data forwarding stage. In the HTTP/3 layer, a bug caused malformed chunked encoding when proxying an H3 request without Content-Length to an HTTP/1 backend. The H3 mux was assigning the cumulative data length to the stream endpoint's known input payload instead of incrementing it, producing incorrect chunk sizes for multi-frame bodies. PUSH_PROMISE frames on backend connections were also not handled properly. Additionally, a length field calculation bug was fixed in QUIC Initial packets: when padding is added to a small packet, the varint encoding of the Length field grows by one byte, but the length value itself was not adjusted accordingly, causing peers to reject such packets. A separate issue limited the QUIC backend-side rxbuf to a single buffer, causing significant latency when retrieving large objects; this is now corrected. Finally, xprt_qmux was missing the ->get_ssl_sock_ctx() method, causing conn_get_ssl_sock_ctx() to return NULL during the qmux handshake and leading to a crash when a TLS alert was received from the peer. Several fixes were applied to the hq-interop HTTP/0.9 transcoder used for QUIC backend debugging. Response buffer wrapping was not handled, large responses with a full demux buffer were not properly paused, streams with unknown payload length were incorrectly closed with RESET_STREAM instead of FIN, and a missing buffer-full check could trigger a BUG_ON() assertion. Three multiplexers were affected by the same draining bug: when trying to send data to a server whose stream was closed (in error or half-closed state), the mux claimed to have consumed the buffered data without actually draining them. This means the upper stream would keep trying to send these data in a loop, potentially triggering the watchdog. The FCGI, H2, and SPOP multiplexers were all fixed. In the FCGI mux, an integer overflow was fixed: the unsigned 16-bits integer used for the record length overflowed to 0 when the record length was 65535 and padding was non-zero, causing the state machine to skip consuming buffer data and parse the remaining bytes as a new FCGI record header. This could only happen for unknown records or for STDOUT records received after the end of the response. CVE-2026-55203 was assigned to this overflow. It must be noted however that the assigned severity is excessively high relative to the actual exploitability: triggering this bug requires the FCGI backend server itself to be compromised and to craft a malicious response with exactly those values, which is well outside a normal threat model where the backend is trusted. A regression was fixed in the Lua HTTP applet: after migrating it to its own internal buffers, the EOS flag was no longer set when the applet exited. This caused streams with unknown payload size (i.e. with no content-length or transfer-encoding) to block indefinitely waiting for an end-of-stream signal that would never come. When L7 retries were configured and the maximum retry count was reached on a reused connection, the stream was silently closed on the client side to let it safely retry the request instead of returning a 502 or 425 error. The SF_SRV_REUSED flag is now ignored in this path so that the error is properly reported to the client. Additionally, the set-headers-bin and add-headers-bin HTTP actions were not making a copy of the sample expression result before manipulating the HTX message; since both use the same static trash chunk, internal defragmentation could silently overwrite the sample data, leading to incorrect header values. Two SSL/TLS fixes are included. With kTLS, the TLS ULP was being enabled on the socket before the TCP connection was fully established; the Linux kernel requires TCP_ESTABLISHED state for this to succeed, so the operation is now deferred until the TLS handshake completes. With TLS 1.3 0-RTT, the early data buffer was freed when emptied, but more early data could still arrive; the buffer is now reset rather than freed until all early data has been received. In the ACME client, when a CA returned an authorization with status already "valid" (e.g. because a domain was recently validated), the auth->ready flag was never initialized, keeping it at 0. This caused the task to wait indefinitely for a readiness signal that will never arrive, since no challenge was published and no external agent will ever call challenge_ready() for that domain Two startup bugs were fixed. Since 3.3, the PCRE2 thread-local match array was initialized too late in the boot sequence, after lua-load, meaning that any Lua script using a regex with PCRE2_JIT would crash. The initialization now happens early enough to cover the Lua loading phase. Separately, thread detection code referenced a variable that is only available when threads are enabled, breaking the build without thread support. A NULL pointer dereference was fixed in the HPACK table code: when the memory pool was exhausted, hpack_dht_defrag() could return NULL and be immediately dereferenced, crashing the worker process. CVE-2026-55204 was assigned to this issue. Here again the assigned severity is excessively high: the condition only arises when the process is already out of memory, at which point the allocator failure itself is likely to cause other problems before this specific code path is even reached. The probability of hitting this particular NULL dereference in a production environment is extremely thin, and a crash under memory exhaustion, while undesirable, is not a meaningful security primitive an attacker could reliably control or exploit. Finally, a few minor issues were also addressed: the error message for ACL identifier lookup failures incorrectly said "map" instead of "ACL"; ha_diag_warning() was misused to report thread creation counts, causing false positives with -dW; a new ha_diag_notice() function was introduced to avoid this; a relative file path for the 'h1-case-adjust-file' directive was not resolved to an absolute path at parse time, causing failures with multiple config files; adding a server with consistent hash balancing was broken; and a leftover debugging memset was removed from the redirect path. Thanks to Andrea Cocito who fixed the HTTP/3 payload length accounting issue causing malformed chunked encoding to HTTP/1 backends, to Karol Kucharski who fixed the kTLS regression where the TLS ULP was enabled before the TCP connection was established, to Tristan Madani who found and fixed both the FCGI integer overflow (CVE-2026-55203) and the HPACK NULL pointer dereference (CVE-2026-55204) and to Thayne McCombs for his Lua documentation fix. And of course, thanks to everyone for your help on this release! 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.4/src/ Git repository : https://git.haproxy.org/git/haproxy-3.4.git/ Git Web browsing : https://git.haproxy.org/?p=haproxy-3.4.git Changelog : https://www.haproxy.org/download/3.4/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 --- Complete changelog : Amaury Denoyelle (10): BUG/MEDIUM: mux_quic: prevent risk of infinite loop on recv BUG/MINOR: mux_quic: do not interrupt recv on error/incomplete data BUG/MINOR: quic: fix Initial length value in sent packets BUG/MINOR: server: fix add server with consistent hash balancing BUG/MINOR: quic: fix rxbuf settings on backend side BUG/MEDIUM: mux_quic: fix freeze transfer after QCS rxbuf realign BUG/MINOR: hq-interop: reject too big content BUG/MINOR: hq-interop: prevent reset if missing content-length BUG/MINOR: hq-interop: support full demux buf on large response BUG/MINOR: hq-interop: support response buffer wrapping Andrea Cocito (2): BUG/MEDIUM: h3: increment unknown request payload length REGTESTS: quic: test H3 request without content-length Christopher Faulet (16): BUG/MEDIUM: check: Skip tcpcheck post-config for external checks BUG/MEDIUM: check: Ignore small-buffer option when starting an external check MINOR: check: Don't dump buffers state in check traces for external checks BUG/MEDIUM: server/checks: Support healtcheck keyword on default-server lines BUG/MINOR: tcpcheck: Override external check if healthcheck section is set REGTESTS: checks: Add script for external healthchecks REGTESTS: Fix log matching in healthcheck-section.vtc BUG/MINOR: http-ana: Remove a debugging memset on redirect BUG/MEDIUM: http-ana: Don't ignore L7 retry errors BUG/MINOR: mux-h1: Properly resolve file path for 'h1-case-adjust-file' BUG/MEDIUM: http-act: Make a copy of the sample expr in (set/add)-headers-bin BUG/MEDIUM: hlua: Properly report EOS when http applet exits BUG/MEDIUM: mux-fcgi: Truly drain outgoing HTX data when the stream is closed BUG/MEDIUM: mux-h2: Truly drain outgoing HTX data when the stream is closed BUG/MEDIUM: mux-spop: Truly drain outgoing data when the stream is closed BUG/MEDIUM: mux-quic: Drain the given amount of data in qcs_http_reset_buf() Karol Kucharski (1): BUG/MEDIUM: ktls: defer enabling TLS ULP on a socket until connected Olivier Houchard (4): BUG/MEDIUM: threads: Fiw build when using no thread BUG/MEDIUM: checks: Dequeue checks on purge BUG/MEDIUM: h3: Properly handle PUSH_PROMISE on backend connections BUG/MEDIUM: ssl: Don't free the early data buffer too early Thayne McCombs (1): DOC: lua: remove incorrect init tags Tristan Madani (2): BUG/MINOR: hpack-tbl: add missing NULL check after hpack_dht_defrag() BUG/MEDIUM: mux-fcgi: fix uint16_t overflow in drl += drp William Lallemand (1): BUG/MEDIUM: acme: stuck ACME task when authz is already "valid" Willy Tarreau (6): BUG/MEDIUM: regex: initialize the match array earlier during boot BUG/MEDIUM: xprt_qmux: implement ->get_ssl_sock_ctx() to get the SSL laye CLEANUP: sessions: simplify the sess_priv_conns pool name BUG/MINOR: acl: report "ACL" not "map" in ACL ID lookup failures MINOR: errors: add ha_diag_notice() to report diag-level notifications BUG/MINOR: cpu-topo: use ha_diag_notice() to report thread creations -- Christopher Faulet

