Hi,
HAProxy 3.5-dev9 was released on 2026/10/14. It added 239 new commits
after version 3.5-dev8.
This release contains ~56 bug fixes, 3 of which need particular care:
- quic: a risk of crash exists in certain conditions of a terminated
stream. To be honest I don't remember he details, it's super rare
but did happen (was reported by a user).
- HTTP/2: an HTTP/2 PING frame cannot be entirely consumed until it is
complete, and the presence of an incomplete frame is sufficient to
cause a wakeup, preventing the CPU from going idle until the entire
frame is received, and resulting in a 100% CPU measurement for as long
as the peer keeps the connection open without completing the frame (or
until "timeout http-request" expired, when one is set). The performance
impact is moderate and depends on the ratio of such connections among
all concurrent connections. Partial PING frames are now detected as
short reads like the other frame types, so the tasklet goes back to
sleep until the rest of the frame arrives. The handling of short reads
was fixed in 2.5, but PING was missed; it only became a problem in 3.1
when the tasklet wake-up logic changed. As a workaround on an older
build, setting "timeout http-request" to a low value such as 1-5
seconds limits the CPU consumption. A CVE was request, this affects
stable versions as well.
- HTTP/1.1: for chunked messages, when a chunk-size line was longer than
the input buffer, the parser made no progress, waiting for the end of
the line. So no data was consumed, the buffer was full and could not be
extended: the same bytes were retried endlessly with no request ever
processed on it, preventing, as for the H2 multiplexer issue, the CPU
from going idle. Here too, the impact is moderate and depends on the
ratio of such connections among all concurrent connections. A parsing
error (HTTP 400) is now reported as soon as the parser determines that
the line cannot possibly fit, using the same full-buffer detection that
already exists for oversized headers and trailers. Reported with a fix
proposal by Adam Crawford. A CVE was request, this affects stable
versions as well.
Aside this, there's fortunately quite some new stuff:
- HTTP: some users trying haproxy as an AI gateway experienced huge
latencies (milliseconds) due to missing "option http-no-delay". It
turns out that in the case of server-streamed events (SSE), there's a
recognizable content-type ("text/event-stream") in the response, so we
now check for it in order to automatically switch to low-latency mode.
One less option to care about, and it also automatically adapts to the
response contents so that we don't degrade a whole proxy when a single
URL requires it. A small utility (dev/sse-latency) was also developed to
measure the latency across a gateway such as haproxy, showing a few tens
of microseconds in idle (actually the time it takes to wake the CPU up
from idle and boosting its frequency between messages) and no longer
anything expressed in milliseconds.
- OCSP: a new "do-ocsp-request" tcp/http action was introduced, to perform
an OCSP request on the client certificate on the fly so that it's
possible to verify its status before going further, and even to just
deny the request (by default) in case of bad status.
- dynamic backends: new CLI actions were added to greatly simplify the
management of dynamic backends. All of these rely on a new background
task to complete the operation, waiting for remaining references to
disappear, so the script/API client will no longer need to poll for
each server removal one at a time nor have to care about the proper
completion of an operation:
- purge server <be>/<srv>: just like "del server" except that the
action succeeds and programs the removal in background. Enabling
the server again cancels the operation.
- purge all servers <be>: just like "purge server", but does it for
all servers in the backend, and ignores those that cannot be deleted
due to configuration constraints.
- purge backend <be>: like "del backend" but deletes all servers in
background first, then programs the deletion of the backend. A
publish/unpublish command will cancel the operation.
- wait ... srv-unknown / wait ... be-unknown: waits for the server
or backend name to no longer be found (i.e. guaranteed removal).
- debug: haproxy's symbols table is now stored compressed at build time
inside the executable (quite small), is accessible from gdb even on
stripped binaries, but is also accessible from haproxy itself, meaning
that warnings and panic dumps will now show much more precise backtraces.
Also, the "set-dumpable" keyword now automatically disables the implicit
automatic chroot, otherwise there's no way to dump a core anymore.
- filters: the new API allowing a filter to decide where it will register
itself with regards to the other ones is finally in place, and with it,
the filter-sequence directives have been re-enabled. The default sequence
matches the vast majority of use cases, and it is possible to reposition
certain filters (e.g. one might want to decompress after caching instead
of before so as to store smaller objects). It also allows to instantiate
multiple occurrences of the trace filter each at their specific location
(before or after an existing filter family), and to separately configure
and enable a filter. This is also particularly important with dynamic
backends so that all this stuff is correct by default or can be properly
configured in the relevant defaults sections. Most users won't notice
(which is also why it was tricky), but it means that this part is finally
expected to be over.
- SPOE: a new "on-switching-rules" event permits an SPOA to influence the
routing decision to a backend, by being called just at the moment the
backend selection is performed.
- the remaining old keywords in cfgparse-global and cfgparse-listen were
finally migrated to the keyword registration API meaning that we no
longer need to keep some lists of exceptions for historic keywords that
were not listed anywhere. "haproxy -dKcfg" now lists something like 125
extra keywords that were not referenced before, which will be helpful to
those watching for new keywords, but more importantly, the legacy code
no longer exists to serve as example when new features are developed.
It's possible that some more dead code could be removed but that was not
deeply explored yet.
- a new section "loggers" was added, in order to group multiple logging
destinations and reference them in "log" directives and "do-log" actions.
This will permit to force certain logs to be emitted to certain
destinations on certain conditions.
- Lua: a new PROXY event family was added to watch for backend addition/
deletion.
- SSL: a new curve2name() converter was added to convert curve names
between the various standards, e.g. "prime256v1" and "P-256" are the
same.
- CLI: "show info" now shows the SSL library's FIPS mode status.
- the rest is as usual: regtests, doc, build fixes, and a bunch of doc
updates.
As a reminder, if you're running a stable version, please see the
respective announcements and update yours regarding the two vulnerabilities
above.
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 (41):
BUG/MINOR: proxy: add PR_CAP_LB for dynamically added backend
BUG/MINOR: server: fix "del server" description
BUG/MINOR: cli: update wait help usage
BUG/MEDIUM: server: remove immediately from GUID tree on delete server
DOC: server: explicit refcount interaction in "del server"
BUG/MINOR: hq-interop: prevent double EOM on request with standalone FIN
BUG/MINOR: ssl: fix trace crash on FE conn release in io cb
BUG/MINOR: ssl: fix trace crash on BE conn release in io cb
BUG/MAJOR: mux-quic: prevent crash on QCS already set for purging
BUG/MINOR: mux_quic: prevent purgeable QCS lingering
BUG/MINOR: mux_quic: extend FINAL_SIZE_ERROR detection on stream
BUG/MINOR: mux_quic: report conn error on RESET_STREAM fctl violation
BUG/MEDIUM: mux_quic: account discarded data in connection flow control
BUG/MINOR: mux_quic: account data in conn fctl on early detach
BUG/MINOR: h3: reject frames with truncated header
BUG/MINOR: h3: close connection on GOAWAY frame too long
BUG/MINOR: h3: reject RS/SS for QPACK uni streams
BUG/MEDIUM: mux_quic: notify stream on RESET_STREAM reception
BUG/MEDIUM: h3: fix end-of-input report on DATA wrapping
MEDIUM: h3: do not close conn on unaligned non DATA frame
BUG/MEDIUM: h3: fix incorrect DATA size block on DEM FULL
OPTIM/MINOR: server: restrict <all_servers> list usage
MINOR: server: rename <all_servers> list to <servers_to_init>
MINOR: server: refactor cli_find_server()
MINOR: server: extract unregistering from "delete server" CLI parser
MINOR: proxy: define purge task and purge servers list
MEDIUM: server: implement "purge server" CLI
MINOR: server: set maintenance mode on "purge server"
MINOR: proxy: yield during servers purge
MINOR: cli: refactor CLI wait command parser
MINOR: cli: implement wait srv-unknown
OPTIM/MINOR: server: remove unnecessary server lookup during purge
REGTESTS: server: test "purge server" CLI command
MEDIUM: server: implement "purge all servers" CLI
MINOR: server: yield during "purge all servers"
REGTESTS: server: test "purge all servers" CLI command
MINOR: proxy: extract proxy runtime deletion in a specific function
MEDIUM: proxy: implement purge backend
MINOR: cli: implement wait be-unknown
OPTIM/MINOR: proxy: remove unnecessary lookup during purge
REGTESTS: proxy: test "purge backend" CLI
Aurelien DARRAGON (11):
BUG/MINOR: log: fix erroneous error message in log-steps parser
MINOR: log: add "loggers" section
MINOR: log: support 'to' parameter for "log" directive
MINOR: log: support explicit <loggers> list for do_log_ctx()
MINOR: log: support 'to' parameter fo do-log action
DOC: config: document new "loggers" section and relevant uses
BUG/MINOR: hlua: assume proxy_find_by_id() may return NULL on previously
existing server
MINOR: event_hdl: add PROXY family events
MINOR: proxy: add e_sub proxy subscrition list
MINOR: event_hdl/proxy: publish EVENT_HDL_SUB_PROXY_{ADD,DEL,PUB,UNPUB}
events
MINOR: hlua: expose PROXY event family through Lua
Benedict Weis (1):
MEDIUM: spoe: add the "on-switching-rules" event
Christopher Faulet (65):
BUG/MINOR: spoe: Fix the message list splice with an empty list
BUG/MINOR: flt-decomp: Fix the decompression filter keyword list name
MEDIUM: flt-http-comp: Enable compression directions from algorithms
MINOR: flt-http-comp: Use compression options as filter config
BUG/MINOR: flt-http-comp: Enable implicit filters independently per
direction
MINOR: flt-http-comp: Don't uselessly init a compression filter
MINOR: flt-decomp: Don't instantiate a filter without applicable direction
MINOR: bwlim: Use a different filter ID for in/out filters
MINOR: fcgi: Move application validation to the post-check stages
MINOR: filters: Introduce filter classes and per-side class ordering
MINOR: filters: Add per-proxy filter instance storage and lookup
MINOR: filters: Inherit filter instances from defaults sections
MINOR: filters: Add instance parsers for the built-in classes
MEDIUM: filters: Parse and finalize filter-config instances
MINOR: filters: Add filter-enable and filter-disable directives
MINOR: filters: Add per-side filter-sequence constraints
MINOR: filters: Mirror implicit filters as filter instances
MINOR: filters: Expose classes and instances through the CLI
MINOR: filters: Reject filter-* directives in anonymous defaults
MINOR: filters: Restrict defaults inheritance with explicit filter
directives
MINOR: filters: Reject mixing legacy and filter-* directives
MINOR: filters: Maintain a global instance list per proxy
MINOR: filters: Use global instance order for startup operations
MINOR: filters: Support global instance sequencing
MINOR: filters: Expose global filter ordering through the CLI
MINOR: filters: Use a proxy flag to detect use of legacy 'filter'
directive
MINOR: filters: Don't finalize filter instances in legacy mode
MINOR: filters: Add helpers to iterate and find filter configurations
MINOR: filters: Add flt_has_filter() and use it in proxy_finalize()
MINOR: filters: Select legacy or instance model in lifecycle callbacks
MEDIUM: filters: Attach filter instances to streams
MEDIUM: cache: Resolve cache rules during the check stage
MINOR: cache: Use the filter helpers for cache checks and post-check
MINOR: fcgi-app: Use the filter configuration helpers for lookups and
checks
MINOR: bwlim: Look up the bandwidth filter with the filter helpers
MINOR: flt-decomp: Use the filter helpers for implicit decompression
checks
MINOR: flt-http-comp: Use the filter helpers for implicit compression
checks
MINOR: spoe: Use the filter configuration helpers for lookups and checks
REGTESTS: filters: Add a test for per-proxy stream filter ordering
REGTESTS: filters: Exercise Lua inheritance and state selection
REGTESTS: filters: Exercise stream isolation across backend modes
REGTESTS: filters: Exercise Lua callback resumption
REGTESTS: filters: Exercise Lua filters across HTTP retries
REGTESTS: filters: Exercise Lua probes around mixed built-in filters
REGTESTS: filters: Exercise TCP Lua payload chains and sequencing
REGTESTS: filters: Exercise per-thread Lua instance state
REGTESTS: filters: Record Lua callback waits across backend EOF
REGTESTS: filters: Check non-HTX Lua filters in HTTP chains
MINOR: filters: Add a global option to enforce the legacy filter api
DOC: filters: Document the filter instance mode and the legacy mode
DOC: lua: Document the filter instance declaration of Lua filters
CLEANUP: filters: Use the stream filter instance wording in comments
MINOR: filters: List the filter classes in haproxy -vv
DOC: config: Review "Supported filters" section
MINOR: lua: Protect the common Lua state during a runtime filter deinit
MEDIUM: stream: Hold a reference on the frontend during stream_free()
MINOR: applet: Add a destroy callback to the applets
BUG/MEDIUM: spoe: Let an agent outlive its applets during a backend
deletion
MEDIUM: filters: Activate the instances inherited by dynamic backends
DOC: filters: Document how to port an existing filter
BUG/MEDIUM: mux-h1: also release the embryonic stream when the error is
empty
BUG/MINOR: http-fetch: don't report pseudo-headers in TCP mode
BUG/MEDIUM: mux-h1: only accept the H2 preface on the first request
REGTESTS: http-messaging: Fix http_fail_cnt_body to succeed with small
timeouts
BUG/MAJOR: h1-htx: report an error on a too long chunk-size line
Frederic Lecaille (2):
BUG/MEDIUM: ssl: crt-list ciphers ignored with AWS-LC
BUG/MEDIUM: ssl: crt-list curves ignored with AWS-LC
Lukas Tribus (1):
MINOR: doc: elaborate on diagnostic mode behavior
Olivier Houchard (13):
CLEANUP: sym: move symbol resolution out of tools.c
MINOR: sym: add an in-process symbol table for backtraces
MINOR: sym: collect the symbols known to the dynamic linker
MINOR: sym: add helpers to collect the symbols of an ELF image
IMPORT: xz: import the xz-embedded decoder
MEDIUM: sym: store a compressed symbol table in a loadable section
MINOR: sym: read local symbols and debug info from ELF files
MINOR: sym: also read the compressed symbol tables from the ELF files
OPTIM: sym: skip the embedded table when the file provided the symbols
MINOR: makefile: add CROSS_TOOLCHAIN to prefix the target's tools
BUILD: Hopefully fix build on FreeBSD
BUILD: sym: Define HA_HAVE_DL_ITERATE_PHDR on FreeBSD
BUG/MEDIUM: listener: Consider maxconn before waking listeners
Remi Tricot-Le Breton (10):
MINOR: ocsp: Remove free of main structure from ssl_sock_free_ocsp_data
MINOR: ocsp: Send OCSP response status back to caller
MINOR: ocsp: Raise specific ocsp error code in ocsp parsing/verifying
functions
MINOR: ocsp: Dup OCSP_CERTID in ssl_ocsp_create_request_details
MEDIUM: ocsp: Create new do-ocsp-request action
MINOR: ocsl: Add 'inapplicable' return code to client certificate ocsp
check
DOC: ocsp: Add doc for the new 'do-ocsp-request' action
REGTESTS: ocsp: Add 'do-ocsp-request' tests
BUG/MINOR: ssl: Fix potential NULL-deref in "dump ssl cert" with AWS-LC
MINOR: cache: Remove a useless variable in http_action_req_cache_use
William Lallemand (21):
BUILD: ssl: fix build without USE_OPENSSL + clear_ocsp_clt_check_ctx
REGTESTS: cli: don't check the HAProxy string
MINOR: ssl: support curve fetches with AWS-LC API 30
BUILD: ssl: avoid mixed declarations in the AWS-LC curve fetch
CI: github: test AWS-LC 3.x and 4.x LTS branches
CI: github: test the latest AWS-LC release on development branches
CI: github: cache QuicTLS builds
CI: github: move wolfSSL 5.7.0 testing to a nightly workflow
BUG/MINOR: ssl: don't uppercase the native group name
DOC: ssl: clarify the NID-based curve fetch branch
BUG/MINOR: ssl: use standard names for X25519 and X448 groups
MINOR: ssl: split curve names into ANSI, IANA and NIST columns
MINOR: ssl: skip unavailable NIDs in curves2nid lookup
MINOR: ssl: complete registered group names and library NID mappings
MINOR: ssl: add curve2name converter
MINOR: ssl: accept integer group IDs in curve2name
MINOR: stats: report the SSL library FIPS mode in "show info"
REGTEST: ssl: require OpenSSL 1.1.1 for crt-list ciphersuites tests
DOC: make: fix EXTRA_MAKE fragment filename
BUG/MINOR: cfgparse-global: only warn when "localpeer" is set together
with -L
DOC: configuration: document AWS-LC compatibility restrictions
Willy Tarreau (70):
DEV: sse-latency: add a latency measuring tool for SSE response streams
MINOR: http-ana: automatically enable http-no-delay for streaming events
(SSE)
DEBUG: support reading set-dumpable from HAPROXY_FORCE_DUMPABLE
BUG/MAJOR: cfgparse: properly check userlist section when scanning
keywords
BUG/MINOR: cfgparse-global: add missing gotos to error label in
max-spread-checks
BUG/MINOR: cfgparse-global: add missing calloc() check for the global
description
BUG/MINOR: cfgparse-listen: release the previous description of the proxy
BUG/MINOR: cfgparse-listen: fix the argument checked by "email-alert"
BUG/MINOR: cfgparse-listen: fix quoting of the condition conflict warning
BUG/MINOR: cfgparse-global: reject extra arguments to numa-cpu-mapping
and anonkey
BUG/MINOR: cfgparse-listen: always reject extra arguments to "persist"
BUG/MINOR: cfgparse-listen: don't lose the errors reported while creating
a proxy
BUG/MINOR: cfgparse-listen: properly reject extra args in a backend
section
BUG/MINOR: proxy: also look for the closest option among cfg_opts3
BUG/MINOR: cfgparse-listen: complete the list of options for the
suggestions
CLEANUP: proxy: adapt the error message for removed "option transparent"
MINOR: cfgparse: expose the keyword modifier to the keyword parsers
CLEANUP: cfgparse-global: move the argument-less boolean options to a
parser
CLEANUP: cfgparse-global: move "set-dumpable" to its own parser
CLEANUP: cfgparse-global: move "uid", "gid", "user" and "group" to its
own parser
CLEANUP: cfgparse-global: move "external-check" to its own parser
CLEANUP: cfgparse-global: move "cluster-secret" to its own parser
CLEANUP: cfgparse-global: move the process-wide limits to a single parser
CLEANUP: cfgparse-global: move the compression limits to a single parser
CLEANUP: cfgparse-global: move "ssl-server-verify" to its own parser
CLEANUP: cfgparse-global: move "node" and "description" to their own
parser
CLEANUP: cfgparse-global: move "unix-bind" to its own parser
CLEANUP: cfgparse-global: move the log keywords to a single parser
CLEANUP: cfgparse-global: move the state file keywords to their own parser
CLEANUP: cfgparse-global: move the spread-checks keywords to their own
parser
CLEANUP: cfgparse-global: move "cpu-map" to its own parser
CLEANUP: cfgparse-global: move "anonkey" to its own parser
CLEANUP: cfgparse-global: ask not to add new keywords in common_kw_list[]
CLEANUP: cfgparse-global: simplify cfg_parse_global()
MINOR: cfgparse-listen: introduce proxy_parse_removed_kw() and use it
MEDIUM: cfgparse-listen: move removed keywords' help messages into an
array
CLEANUP: cfgparse-listen: move "disabled" and "enabled" to their own
parser
CLEANUP: cfgparse-listen: move the connection limits to thier own parser
CLEANUP: cfgparse-listen: move the backend one-arg keywords to their own
parser
CLEANUP: cfgparse-listen: move "mode" to its own parser
CLEANUP: cfgparse-listen: move "id" and "description" to their own parser
CLEANUP: cfgparse-listen: move "acl" to its own parser
CLEANUP: cfgparse-listen: move the monitor keywords to their own parser
CLEANUP: cfgparse-listen: move the persistence keywords to their own
parser
CLEANUP: cfgparse-listen: move "capture" to its own parser
CLEANUP: cfgparse-listen: move the log format keywords to their own parser
CLEANUP: cfgparse-listen: add parser for "log", "log-tag" and
"unique-id-header"
CLEANUP: cfgparse-listen: move the load-balancing keywords to their own
parser
CLEANUP: cfgparse-listen: move "source" "usesrc" and "interface" to their
own parser
CLEANUP: cfgparse-listen: add parser for server/backend sekection keywords
CLEANUP: cfgparse-listen: move the HTTP rule sets to their own parser
CLEANUP: cfgparse-listen: move "redirect" to its own parser
CLEANUP: cfgparse-listen: move "stick-table" to its own parser
CLEANUP: cfgparse-listen: move "stick" to its own parser
CLEANUP: cfgparse-listen: move "cookie" to its own parser
CLENAUP: cfgparse-listen: move "email-alert" to its own parser
CLEANUP: cfgparse-listen: move "stats" to its own parser
CLENAUP: cfgparse-listen: move the "server" keywords to their own parser
CLEANUP: cfgparse-listen: move "bind" to its own parser
MINOR: log: add proxy_logformat_origin() to detect a proxy's log-format
type
CLEANUP: cfgparse-listen: move "option" to its own parser
CLEANUP: cfgparse-listen: simplify the second half of cfg_parse_listen()
MINOR: cfgparse: always suggest the section names on an unknown keyword
CLEANUP: cfgparse: remove the unused common_kw_list[] definitions
BUG/MEDIUM: mux-h2: don't return a freed tasklet from h2_deferred_shut()
BUG/MEDIUM: mux-fcgi: don't return a freed tasklet from
fcgi_deferred_shut()
BUG/MEDIUM: mux-h1: don't pass an upgraded connection to traces in
h1s_destroy()
BUG/MEDIUM: mux-h2: report a dead rhttp connection in h2_wake()
BUG/MEDIUM: mux-h2: fix a possible crash on rxbuf allocation failure
BUG/MAJOR: mux-h2: properly handle short reads on PING frames
Yeonggi Kim (4):
BUG/MEDIUM: cli: fix the last command detection on a full command line
REGTESTS: cli: test a command line of exactly tune.bufsize bytes
BUG/MEDIUM: log: do not ignore the peer's shutdown in the syslog applet
REGTESTS: log: check that a log-forward TCP session ends on client close
---