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.
At a glance
| CVE ID | CVE-2026-54763 |
|---|---|
| Affected component | The BasicAuth, DigestAuth and ForwardAuth middlewares of Traefik (an HTTP reverse proxy and load balancer) |
| Affected versions | below 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 versions | v2.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) |
| CWE | CWE-178 (improper handling of case sensitivity), CWE-290 and CWE-345, all assigned by the CNA |
| NVD status | Analyzed (published 2026-07-06, last modified 2026-07-08) |
| In the CISA KEV catalog | No (verified against the 3 August 2026 catalog) |
What happens: the asymmetry between hyphens and underscores
The official description reads:
The problem lives where three facts overlap.
- 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.
- 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. - 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
authResponseHeaderspath, instead of it. The application then reads it as if it were the trusted header.
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:
The PHP manual entry for $_SERVER says the same thing:
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:
- Syntax:
underscores_in_headers on | off; - Default:
underscores_in_headers off; - Context:
http,server
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.
underscoreHeadersStrategy, with three possible values:
keep- the default. Request headers are forwarded as isdelete- any request header whose name contains an underscore is silently removedreject- the request is rejected with 400 Bad Request
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.
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 by | Version | Score | Vector |
|---|---|---|---|
| NVD (nvd@nist.gov, Primary) | CVSS 3.1 | 10.0 CRITICAL | AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N |
| CNA (GitHub, Secondary) | CVSS 4.0 | 7.8 HIGH | AV: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.
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.
- 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. - Check whether you use an affected middleware. Look for
basicAuth,digestAuthorforwardAuthin your dynamic configuration, and whether they are configured to pass the authenticated identity to the backend (an option naming a header, or ForwardAuth'sauthResponseHeaders). If no identity is passed downstream, this path is not exposed. - 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.
- Check the entry point configuration. After upgrading, confirm that you explicitly set
underscoreHeadersStrategyon the relevant entry points. If you did not, it remainskeep, the previous behaviour. - If nginx sits in front, check its setting. Has
underscores_in_headersbeen explicitly turnedon? Configurations sometimes carry an oldonadded 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.
- Case sensitivity. HTTP header names are case-insensitive, but code that handles them by hand tends to make them case-sensitive. That is exactly CWE-178 here.
- Separator equivalence. Hyphen versus underscore, space versus plus, duplicated slashes.
- Unicode normalisation. Characters that look identical but differ in code points. Are you normalising before comparing?
- Percent-decoding depth. Which layer decodes, and how many times.
- Path comparison including trailing slashes and case. Especially where access control is done on path strings.
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
- Checked on 4 August 2026
- NVD API (CVSS, CWE, CPE, status, dates): https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-54763
- CVE Program record (description, affected ranges, CNA-assigned score): https://cveawg.mitre.org/api/cve/CVE-2026-54763
- Traefik advisory: https://github.com/traefik/traefik/security/advisories/GHSA-x677-9fxg-v5c5
- Traefik v3 migration documentation (values and default of
underscoreHeadersStrategy): https://doc.traefik.io/traefik/v3.6/migrate/v3/, together with the v3.6.22 source filepkg/config/static/entrypoints.go - nginx documentation (
underscores_in_headers): https://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers - RFC 3875 section 4.1.18 (how CGI meta-variable names are built): https://www.rfc-editor.org/rfc/rfc3875.txt
- PHP manual,
$_SERVER: https://www.php.net/manual/en/reserved.variables.server.php - CISA Known Exploited Vulnerabilities catalog (version 2026.08.03, 1,657 entries; confirmed not listed): https://www.cisa.gov/known-exploited-vulnerabilities-catalog
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.