CVE-2026-23941 is an HTTP request smuggling flaw (CVSS 9.4 in NVD) in the Erlang OTP inets httpd module. It uses the first of duplicate Content-Length headers, while many reverse proxies use the last. Fixed releases are OTP 28.4.1, 27.3.4.9 and 26.2.5.18. Upgrade, or apply the proxy and keep-alive workarounds.
What's affected
Erlang/OTP from 17.0 before the fixed releases: 28.4.1, 27.3.4.9 and 26.2.5.18, according to the Erlang CNA page. The affected component is the httpd server in inets, in the function httpd_request:parse_headers/7. If you do not run the built-in inets httpd server, you are not affected by this issue.
The CNA rates the issue 7.0 (high), while the NVD record scores it 9.4. The difference comes from how much weight is put on the conditions below.
Two conditions apply, per the CNA page. The inets httpd server must sit behind a reverse proxy that honors a different Content-Length header than httpd does, for example last wins instead of first wins. And HTTP keep-alive must be on, which is the default.
How to check if you're exposed
First find out whether you run the inets httpd server at all. It is less common than frameworks such as Cowboy, Bandit or Phoenix, so many Erlang and Elixir services do not use it. Search your release configuration and code for the httpd service or for starting inets with an httpd configuration.
Then check the OTP and inets versions on the node:
erl -noshell -eval 'io:format("~s~n",[erlang:system_info(otp_release)]), halt().'
erl -noshell -eval 'application:load(inets), {ok,V}=application:get_key(inets,vsn), io:format("~s~n",[V]), halt().'
The first command prints only the major release. Use the full OTP version from your package or image tag to compare against 28.4.1, 27.3.4.9 and 26.2.5.18. Finally, identify the proxy in front of it and how it treats duplicate Content-Length headers.
What to do now
- Upgrade OTP to 28.4.1, 27.3.4.9, 26.2.5.18 or later in your series.
- Until you can, configure the front proxy to reject requests that carry more than one Content-Length header.
- Another workaround from the advisory is to turn off keep-alive on httpd with the keep_alive option set to false. It costs performance for clients that make several requests.
- A web application firewall that rejects multiple Content-Length headers is a third option.
- Rebuild and redeploy images that bundle Erlang/OTP.
Why it matters
The weakness is CWE-444, inconsistent interpretation of HTTP requests. RFC 9112 says a message with conflicting Content-Length values should be rejected. When a proxy and a backend disagree on where one request ends, leftover bytes can be read as the start of the next request.
That desynchronization can let one client affect what other users' requests look like to the backend, which can bypass front-end access controls or poison caches. The advisory describes the root cause and workarounds, not exploitation in the wild, and the record does not list this CVE in CISA KEV.
Request smuggling depends on the combination of two components. A scan of one server in isolation will not tell you whether the combination is vulnerable, so look at the whole path from client to backend.
Reading the severity score
The record gives a CVSS base score of 9.4, published 2026-03-13. In the vector, the attacker works over the network, needs no account, and the attack is low in complexity. User interaction is not needed. Impact on confidentiality is high, on integrity high and on availability low.
A base score describes the flaw in general, not your risk. Weigh it against how exposed your instance is and what it can reach. The Gold page for the CVE keeps the score, weakness and references together.
Hardening beyond the patch
Request smuggling is a disagreement between two parsers, so the best long term defense is to make the disagreement impossible. Several steps help.
First, pick a proxy and backend pair that both reject ambiguous requests. RFC 9112 says that a message with conflicting Content-Length values is invalid, and a front end that refuses such requests protects every backend behind it, including ones you forgot about. Check your proxy documentation for a setting that rejects duplicate Content-Length and for Transfer-Encoding conflicts.
Second, review the path between the client and the application. Draw the chain: CDN, load balancer, reverse proxy, application server. Every hop is a place where parsing can differ. Services that sit behind the same front end as the Erlang node should be reviewed too, because smuggling can affect other users of a shared connection.
Third, think about keep-alive. The advisory lists turning keep-alive off on httpd as a workaround because the attack needs a connection that carries more than one request. Disabling it costs performance, but if your proxy opens a fresh backend connection per request, the risk drops. Measure the cost before you decide.
Fourth, test. A simple regression test that sends a request with two Content-Length headers through your real proxy to a staging backend, and expects a 400 response from the proxy, documents the behavior you rely on. Run it again after proxy upgrades.
Finally, record which of your services embed inets httpd. Many Erlang teams use other servers, and knowing which ones use the built-in module tells you how much of your estate this advisory touches.
Quick check
Does this affect every Erlang application?
No. It affects services that use the inets httpd module, and the stated conditions are a reverse proxy that handles duplicate Content-Length differently, plus keep-alive enabled.
Is the proxy workaround enough?
It closes the condition the attack relies on, which is duplicate headers reaching httpd. It is a stopgap. The upgrade fixes httpd itself.
Sources
Last reviewed: 19 October 2026.