CVE-2026-54763 in Traefik: When a Stripped Header Still Reaches the Backend

A reverse proxy strips the headers that would spoof an authenticated identity, and then the header it believed it stripped arrives at the backend anyway. That is CVE-2026-54763. The cause is an asymmetry in how hyphens and underscores are normalised, the kind of thing you cannot spot unless you already know about it. Here are the facts as verified from primary sources, and how to check your own environment.

Checked on 4 August 2026. The version numbers, scores and specifications in this article were verified that day against the NVD API, the CVE Program record, Traefik's advisory and releases, the official migration documentation, the v3.6.22 source, the nginx documentation, RFC 3875 and the PHP manual. No attack steps, proof-of-concept code or concrete header values are given. Only checks you run against your own environment appear here.

At a glance

CVE IDCVE-2026-54763
Affected componentThe BasicAuth, DigestAuth and ForwardAuth middlewares of Traefik (an HTTP reverse proxy and load balancer)
Affected versionsbelow 2.11.51; 3.0.0-beta1 up to but not including 3.6.22; 3.7.0-ea.1 up to but not including 3.7.6
Fixed versionsv2.11.51 / v3.6.22 / v3.7.6
CVSS v3.1 (assigned by NVD)10.0 CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N)
CVSS v4.0 (assigned by the CNA)7.8 HIGH (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N)
CWECWE-178 (improper handling of case sensitivity), CWE-290 and CWE-345, all assigned by the CNA
NVD statusAnalyzed (published 2026-07-06, last modified 2026-07-08)
In the CISA KEV catalogNo (verified against the 3 August 2026 catalog)

What happens: the asymmetry between hyphens and underscores

The official description reads:

Traefik is an HTTP reverse proxy and load balancer. Prior to v2.11.51, v3.6.22, and v3.7.6, Traefik's BasicAuth, DigestAuth, and ForwardAuth middlewares strip canonical-cased spoofed identity headers before writing Traefik's own value, but do not account for underscore-variant header names, which many backends normalize identically to dashed forms.

The problem lives where three facts overlap.

  1. The proxy stripped headers by their canonical form. Before writing the authenticated identity into its own header, Traefik deletes any same-named header the client sent. But Go's header deletion normalises case and hyphens only; the underscore is not treated as a separator. The hyphenated spelling disappears while its underscore-spelled sibling survives.
  2. Backends do not distinguish hyphens from underscores. CGI-style interfaces build a meta-variable name by upper-casing the header name, replacing hyphens with underscores and prefixing HTTP_. The hyphen and underscore spellings therefore become the same name as seen by the backend.
  3. What the proxy believed it removed reaches the backend. The surviving underscore variant arrives alongside the value Traefik intended to set, or, on the ForwardAuth authResponseHeaders path, instead of it. The application then reads it as if it were the trusted header.
Why this shape is dangerous: Traefik's implementation and the backend's implementation each behave exactly as specified in isolation. What is broken is only the joint, where the two definitions of "the same name" disagree. Logs do not help either: the proxy records that it stripped the header and the backend records that it received one, so nothing looks contradictory unless you correlate both sides. A normalisation mismatch across a boundary is the kind of defect you cannot notice from one side alone.

The backend's normalisation rule, from primary sources

Treating hyphens and underscores alike is not an implementation quirk but a documented transformation. RFC 3875 (CGI 1.1), section 4.1.18, states:

Meta-variables with names beginning with "HTTP_" contain values read from the client request header fields, if the protocol used is HTTP. The HTTP header field name is converted to upper case, has all occurrences of "-" replaced with "_" and has "HTTP_" prepended to give the meta-variable name.

The PHP manual entry for $_SERVER says the same thing:

In addition to the elements listed below, PHP will create additional elements with values from request headers. These entries will be named HTTP_ followed by the header name, capitalized and with underscores instead of hyphens.

After this transformation the hyphen and underscore spellings become exactly the same key. If the proxy removed only one of them, the backend reads whichever one survived.

Why nginx has underscores_in_headers

This is not a newly discovered class of problem; it is a known landmine in the reverse proxy world, and nginx has a setting for exactly this. The official documentation (ngx_http_core_module) says:

Enables or disables the use of underscores in client request header fields. When the use of underscores is disabled, request header fields whose names contain underscores are marked as invalid and become subject to the ignore_invalid_headers directive.

The default of off is itself a design-level answer to this risk. Read the detail carefully, though. With off, nginx does not simply delete such headers; it marks them invalid, and what happens next is governed by the ignore_invalid_headers directive. Saying "nginx drops underscore headers by default" is close in outcome but inaccurate as a description of the mechanism.

The fix, and the part that needs attention

The fixed versions are v2.11.51, v3.6.22 and v3.7.6. The v3.6.22 release notes name this CVE among the issues fixed.

Upgrading alone does not change the default behaviour. Checking the v3.6.22 source and the official migration documentation on 4 August 2026, what was added is an entry point option called underscoreHeadersStrategy, with three possible values:
  • keep - the default. Request headers are forwarded as is
  • delete - any request header whose name contains an underscore is silently removed
  • reject - the request is rejected with 400 Bad Request
Because the default is keep, that is, the previous behaviour, entry points that front a backend treating underscores and hyphens identically need delete or reject set explicitly. The security note in the official documentation likewise advises against leaving the default keep in such a configuration.
A discrepancy in the advisory text (verified 4 August 2026): the Summary on the GitHub advisory describes the fix in terms of setting a differently named option to false. However, the option name actually present in the v3.6.22 source (pkg/config/static/entrypoints.go) and in the official migration documentation is underscoreHeadersStrategy. When writing configuration, follow the name in the official documentation and the shipped source, not the wording of the advisory.

On there being two scores

This CVE carries two scores of quite different character. Quoting only one of them changes the impression substantially, so cite each with its source.

Assigned byVersionScoreVector
NVD (nvd@nist.gov, Primary)CVSS 3.110.0 CRITICALAV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
CNA (GitHub, Secondary)CVSS 4.07.8 HIGHAV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N

The 10.0 comes from Scope being rated Changed (S:C) in the CVSS 3.1 vector, which directly reflects the nature of the flaw: it crosses the proxy boundary and corrupts a decision made on the other side.

A caution when quoting: the advisory page contains the reporter's submitted text (the Original Description), which proposes different numbers. Those are the reporter's proposals, not assigned official scores. Only the two in the table above are officially assigned. Automated summarisers have been known to pick up the proposed values and present them as the advisory's score.

The SSVC decision recorded by CISA Coordinator on 8 July 2026 was exploitation: none, automatable: yes, technicalImpact: partial. Searching the 3 August 2026 CISA KEV catalog, this CVE is not listed.

How to check your own environment

All of these are checks against an environment you own.

  1. Determine the Traefik version. Inspect the running binary or the container image tag.
    # Binary
    traefik version
    
    # Container (check both the image tag and the version inside)
    docker ps --format '{{.Image}}'
    docker exec <container> traefik version
    Image tags and actual versions can disagree, so judge by the running version, not the tag.
  2. Check whether you use an affected middleware. Look for basicAuth, digestAuth or forwardAuth in your dynamic configuration, and whether they are configured to pass the authenticated identity to the backend (an option naming a header, or ForwardAuth's authResponseHeaders). If no identity is passed downstream, this path is not exposed.
  3. Check whether the backend equates hyphens and underscores. Anything going through PHP, CGI or FastCGI does, by the RFC 3875 rule above. Some application frameworks apply their own normalisation, so confirm the actual behaviour in the backend implementation.
  4. Check the entry point configuration. After upgrading, confirm that you explicitly set underscoreHeadersStrategy on the relevant entry points. If you did not, it remains keep, the previous behaviour.
  5. If nginx sits in front, check its setting. Has underscores_in_headers been explicitly turned on? Configurations sometimes carry an old on added because "our custom underscore header was not reaching the backend".

Generalising: where to suspect a normalisation mismatch

This defect is less a Traefik-specific mistake than a structural problem: the definition of normalisation changes at every boundary you cross. The same shape of trap hides in places like these.

The countermeasure always takes one shape. Whoever does the stripping should think in allow lists, not deny lists. Design for "only headers approved for the backend get through" rather than "dangerous headers get removed", and missing one spelling variant no longer breaks the model. The reject option newly added to Traefik behaves close to that idea.

Sources and date checked

No attack steps, proof-of-concept code or concrete header values are covered. Anything that could not be verified has been left out.

Frequently Asked Questions

Which versions fix CVE-2026-54763?

Traefik v2.11.51, v3.6.22 and v3.7.6. The affected ranges are below 2.11.51, from 3.0.0-beta1 up to but not including 3.6.22, and from 3.7.0-ea.1 up to but not including 3.7.6 (verified 4 August 2026).

Is upgrading to a fixed version enough on its own?

You also need to configure your entry points. The option added by the fix, underscoreHeadersStrategy, defaults to keep, which forwards headers as before, so entry points fronting a backend that equates underscores and hyphens need delete or reject set explicitly (verified against the source and the official migration documentation on 4 August 2026).

Why is the underscore spelling of a header a problem?

CGI interfaces build a meta-variable name by upper-casing the header name, replacing hyphens with underscores and prefixing HTTP_ (RFC 3875 section 4.1.18). The hyphen and underscore spellings therefore become the same name as seen by the backend. If the proxy removed only the hyphenated one, the underscore variant survives and reaches the backend.

What is the CVSS score for this CVE?

There are two, from different sources. NVD assigned CVSS 3.1 10.0 CRITICAL, and the CNA (GitHub) assigned CVSS 4.0 7.8 HIGH. The advisory page also contains different numbers proposed by the reporter, but those are not officially assigned scores.

← Back to Tech Blog