VYPR
Critical severityNVD Advisory· Published Sep 19, 2024· Updated Sep 20, 2024

HTTP client can remove the X-Forwarded headers in Traefik

CVE-2024-45410

Description

Traefik is a golang, Cloud Native Application Proxy. When a HTTP request is processed by Traefik, certain HTTP headers such as X-Forwarded-Host or X-Forwarded-Port are added by Traefik before the request is routed to the application. For a HTTP client, it should not be possible to remove or modify these headers. Since the application trusts the value of these headers, security implications might arise, if they can be modified. For HTTP/1.1, however, it was found that some of theses custom headers can indeed be removed and in certain cases manipulated. The attack relies on the HTTP/1.1 behavior, that headers can be defined as hop-by-hop via the HTTP Connection header. This issue has been addressed in release versions 2.11.9 and 3.1.3. Users are advised to upgrade. There are no known workarounds for this vulnerability.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Traefik proxy fails to prevent HTTP/1.1 clients from removing or modifying security-critical X-Forwarded headers via the Connection header, enabling spoofing in backend applications.

CVE-2024-45410 is a vulnerability in Traefik, a Golang-based cloud-native application proxy, that allows HTTP/1.1 clients to remove or modify certain X-Forwarded headers (e.g., X-Forwarded-Host, X-Forwarded-Port) which Traefik normally adds before routing requests to backend applications. The root cause lies in Traefik's handling of the HTTP Connection header. According to the HTTP/1.1 specification, headers listed in the Connection header are treated as hop-by-hop headers and may be stripped by intermediaries. An attacker can exploit this by including, for example, Connection: close, X-Forwarded-Host in a request, causing Traefik to remove that header before forwarding the request to the backend [1][2].

Exploitation

Exploitation requires only the ability to send HTTP/1.1 requests to a vulnerable Traefik instance. No authentication is needed as the attack is performed at the HTTP protocol level. The attacker crafts a request with a custom Connection header that lists one or more of the affected X-Forwarded headers (X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Proto, X-Forwarded-Server, X-Real-Ip, X-Forwarded-Tls-Client-Cert, X-Forwarded-Tls-Client-Cert-Info). In certain cases, the attacker may also be able to *modify* these headers if the backend application framework (e.g., Django) transforms hyphens to underscores, as the Connection header mechanism can be combined with other header manipulations [1].

Impact

Since backend applications typically trust X-Forwarded headers for authentication, access control, and routing decisions, an attacker who removes or modifies these headers can impersonate trusted hosts, bypass security checks, or manipulate application behavior. For example, by removing X-Forwarded-Host, an attacker might cause a backend to generate incorrect redirects or expose internal endpoints. The vulnerability is similar in nature to CVE-2022-31813 in Apache HTTP Server [1]. There is no known workaround; the fix is to upgrade Traefik to version 2.11.9 or 3.1.3, which sanitize the Connection header properly [1][2]. The commit at [4] shows the addition of tests ensuring that hop-by-hop headers like X-Forwarded-Host are no longer stripped when sent by the client.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/traefik/traefik/v3Go
>= 3.0.0-beta3, < 3.1.33.1.3
github.com/traefik/traefik/v2Go
< 2.11.92.11.9
github.com/traefik/traefikGo
< 2.11.92.11.9

Affected products

6

Patches

1
584144100524

Cleanup Connection headers before passing the middleware chain

https://github.com/traefik/traefikKevin PolletSep 16, 2024via ghsa
15 files changed · +475 31
  • docs/content/reference/static-configuration/cli-ref.md+3 0 modified
    @@ -111,6 +111,9 @@ Entry point address.
     `--entrypoints.<name>.allowacmebypass`:  
     Enables handling of ACME TLS and HTTP challenges with custom routers. (Default: ```false```)
     
    +`--entrypoints.<name>.forwardedheaders.connection`:  
    +List of Connection headers that are allowed to pass through the middleware chain before being removed.
    +
     `--entrypoints.<name>.forwardedheaders.insecure`:  
     Trust all forwarded headers. (Default: ```false```)
     
    
  • docs/content/reference/static-configuration/env-ref.md+3 0 modified
    @@ -111,6 +111,9 @@ Entry point address.
     `TRAEFIK_ENTRYPOINTS_<NAME>_ALLOWACMEBYPASS`:  
     Enables handling of ACME TLS and HTTP challenges with custom routers. (Default: ```false```)
     
    +`TRAEFIK_ENTRYPOINTS_<NAME>_FORWARDEDHEADERS_CONNECTION`:  
    +List of Connection headers that are allowed to pass through the middleware chain before being removed.
    +
     `TRAEFIK_ENTRYPOINTS_<NAME>_FORWARDEDHEADERS_INSECURE`:  
     Trust all forwarded headers. (Default: ```false```)
     
    
  • docs/content/reference/static-configuration/file.toml+1 0 modified
    @@ -33,6 +33,7 @@
         [entryPoints.EntryPoint0.forwardedHeaders]
           insecure = true
           trustedIPs = ["foobar", "foobar"]
    +      connection = ["foobar", "foobar"]
         [entryPoints.EntryPoint0.http]
           middlewares = ["foobar", "foobar"]
           encodeQuerySemicolons = true
    
  • docs/content/reference/static-configuration/file.yaml+3 0 modified
    @@ -37,6 +37,9 @@ entryPoints:
           trustedIPs:
             - foobar
             - foobar
    +      connection:
    +        - foobar
    +        - foobar
         http:
           redirections:
             entryPoint:
    
  • docs/content/routing/entrypoints.md+34 0 modified
    @@ -422,6 +422,40 @@ You can configure Traefik to trust the forwarded headers information (`X-Forward
         --entryPoints.web.forwardedHeaders.insecure
         ```
     
    +??? info "`forwardedHeaders.connection`"
    +    
    +    As per RFC7230, Traefik respects the Connection options from the client request.
    +    By doing so, it removes any header field(s) listed in the request Connection header and the Connection header field itself when empty.
    +    The removal happens as soon as the request is handled by Traefik,
    +    thus the removed headers are not available when the request passes through the middleware chain.
    +    The `connection` option lists the Connection headers allowed to passthrough the middleware chain before their removal.
    +
    +    ```yaml tab="File (YAML)"
    +    ## Static configuration
    +    entryPoints:
    +      web:
    +        address: ":80"
    +        forwardedHeaders:
    +          connection:
    +            - foobar
    +    ```
    +
    +    ```toml tab="File (TOML)"
    +    ## Static configuration
    +    [entryPoints]
    +      [entryPoints.web]
    +        address = ":80"
    +
    +        [entryPoints.web.forwardedHeaders]
    +          connection = ["foobar"]
    +    ```
    +
    +    ```bash tab="CLI"
    +    ## Static configuration
    +    --entryPoints.web.address=:80
    +    --entryPoints.web.forwardedHeaders.connection=foobar
    +    ```
    +
     ### Transport
     
     #### `respondingTimeouts`
    
  • integration/fixtures/headers/connection_hop_by_hop_headers.toml+37 0 added
    @@ -0,0 +1,37 @@
    +[global]
    +  checkNewVersion = false
    +  sendAnonymousUsage = false
    +
    +[log]
    +  level = "DEBUG"
    +
    +# Limiting the Logs to Specific Fields
    +[accessLog]
    +  format = "json"
    +  filePath = "access.log"
    +
    +  [accessLog.fields.headers.names]
    +    "Foo" = "keep"
    +    "Bar" = "keep"
    +
    +[entryPoints]
    +  [entryPoints.web]
    +    address = ":8000"
    +  [entryPoints.web.forwardedHeaders]
    +    insecure = true
    +    connection = ["Foo"]
    +
    +[providers.file]
    +  filename = "{{ .SelfFilename }}"
    +
    +## dynamic configuration ##
    +
    +[http.routers]
    +  [http.routers.router1]
    +    rule = "Host(`test.localhost`)"
    +    service = "service1"
    +
    +[http.services]
    +  [http.services.service1.loadBalancer]
    +    [[http.services.service1.loadBalancer.servers]]
    +      url = "http://127.0.0.1:9000"
    
  • integration/headers_test.go+53 0 modified
    @@ -4,6 +4,7 @@ import (
     	"net"
     	"net/http"
     	"net/http/httptest"
    +	"os"
     	"testing"
     	"time"
     
    @@ -20,6 +21,11 @@ func TestHeadersSuite(t *testing.T) {
     	suite.Run(t, new(HeadersSuite))
     }
     
    +func (s *HeadersSuite) TearDownTest() {
    +	s.displayTraefikLogFile(traefikTestLogFile)
    +	_ = os.Remove(traefikTestAccessLogFile)
    +}
    +
     func (s *HeadersSuite) TestSimpleConfiguration() {
     	s.traefikCmd(withConfigFile("fixtures/headers/basic.toml"))
     
    @@ -62,6 +68,53 @@ func (s *HeadersSuite) TestReverseProxyHeaderRemoved() {
     	require.NoError(s.T(), err)
     }
     
    +func (s *HeadersSuite) TestConnectionHopByHop() {
    +	file := s.adaptFile("fixtures/headers/connection_hop_by_hop_headers.toml", struct{}{})
    +	s.traefikCmd(withConfigFile(file))
    +
    +	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    +		_, found := r.Header["X-Forwarded-For"]
    +		assert.True(s.T(), found)
    +		xHost, found := r.Header["X-Forwarded-Host"]
    +		assert.True(s.T(), found)
    +		assert.Equal(s.T(), "localhost", xHost[0])
    +
    +		_, found = r.Header["Foo"]
    +		assert.False(s.T(), found)
    +		_, found = r.Header["Bar"]
    +		assert.False(s.T(), found)
    +	})
    +
    +	listener, err := net.Listen("tcp", "127.0.0.1:9000")
    +	require.NoError(s.T(), err)
    +
    +	ts := &httptest.Server{
    +		Listener: listener,
    +		Config:   &http.Server{Handler: handler},
    +	}
    +	ts.Start()
    +	defer ts.Close()
    +
    +	req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
    +	require.NoError(s.T(), err)
    +	req.Host = "test.localhost"
    +	req.Header = http.Header{
    +		"Connection":       {"Foo,Bar,X-Forwarded-For,X-Forwarded-Host"},
    +		"Foo":              {"bar"},
    +		"Bar":              {"foo"},
    +		"X-Forwarded-Host": {"localhost"},
    +	}
    +
    +	err = try.Request(req, time.Second, try.StatusCodeIs(http.StatusOK))
    +	require.NoError(s.T(), err)
    +
    +	accessLog, err := os.ReadFile(traefikTestAccessLogFile)
    +	require.NoError(s.T(), err)
    +
    +	assert.Contains(s.T(), string(accessLog), "\"request_Foo\":\"bar\"")
    +	assert.NotContains(s.T(), string(accessLog), "\"request_Bar\":\"\"")
    +}
    +
     func (s *HeadersSuite) TestCorsResponses() {
     	file := s.adaptFile("fixtures/headers/cors.toml", struct{}{})
     	s.traefikCmd(withConfigFile(file))
    
  • pkg/config/static/entrypoints.go+1 0 modified
    @@ -110,6 +110,7 @@ type TLSConfig struct {
     type ForwardedHeaders struct {
     	Insecure   bool     `description:"Trust all forwarded headers." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true"`
     	TrustedIPs []string `description:"Trust only forwarded headers from selected IPs." json:"trustedIPs,omitempty" toml:"trustedIPs,omitempty" yaml:"trustedIPs,omitempty"`
    +	Connection []string `description:"List of Connection headers that are allowed to pass through the middleware chain before being removed." json:"connection,omitempty" toml:"connection,omitempty" yaml:"connection,omitempty"`
     }
     
     // ProxyProtocol contains Proxy-Protocol configuration.
    
  • pkg/middlewares/auth/connectionheader.go+1 1 renamed
    @@ -1,4 +1,4 @@
    -package connectionheader
    +package auth
     
     import (
     	"net/http"
    
  • pkg/middlewares/auth/connectionheader_test.go+1 1 renamed
    @@ -1,4 +1,4 @@
    -package connectionheader
    +package auth
     
     import (
     	"net/http"
    
  • pkg/middlewares/auth/forward.go+1 2 modified
    @@ -15,7 +15,6 @@ import (
     	"github.com/traefik/traefik/v2/pkg/config/dynamic"
     	"github.com/traefik/traefik/v2/pkg/log"
     	"github.com/traefik/traefik/v2/pkg/middlewares"
    -	"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
     	"github.com/traefik/traefik/v2/pkg/tracing"
     	"github.com/vulcand/oxy/v2/forward"
     	"github.com/vulcand/oxy/v2/utils"
    @@ -90,7 +89,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
     		fa.authResponseHeadersRegex = re
     	}
     
    -	return connectionheader.Remover(fa), nil
    +	return Remover(fa), nil
     }
     
     func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
    
  • pkg/middlewares/forwardedheaders/forwarded_header.go+62 13 modified
    @@ -3,10 +3,13 @@ package forwardedheaders
     import (
     	"net"
     	"net/http"
    +	"net/textproto"
     	"os"
    +	"slices"
     	"strings"
     
     	"github.com/traefik/traefik/v2/pkg/ip"
    +	"golang.org/x/net/http/httpguts"
     )
     
     const (
    @@ -42,19 +45,20 @@ var xHeaders = []string{
     // Unless insecure is set,
     // it first removes all the existing values for those headers if the remote address is not one of the trusted ones.
     type XForwarded struct {
    -	insecure   bool
    -	trustedIps []string
    -	ipChecker  *ip.Checker
    -	next       http.Handler
    -	hostname   string
    +	insecure          bool
    +	trustedIPs        []string
    +	connectionHeaders []string
    +	ipChecker         *ip.Checker
    +	next              http.Handler
    +	hostname          string
     }
     
     // NewXForwarded creates a new XForwarded.
    -func NewXForwarded(insecure bool, trustedIps []string, next http.Handler) (*XForwarded, error) {
    +func NewXForwarded(insecure bool, trustedIPs []string, connectionHeaders []string, next http.Handler) (*XForwarded, error) {
     	var ipChecker *ip.Checker
    -	if len(trustedIps) > 0 {
    +	if len(trustedIPs) > 0 {
     		var err error
    -		ipChecker, err = ip.NewChecker(trustedIps)
    +		ipChecker, err = ip.NewChecker(trustedIPs)
     		if err != nil {
     			return nil, err
     		}
    @@ -66,11 +70,12 @@ func NewXForwarded(insecure bool, trustedIps []string, next http.Handler) (*XFor
     	}
     
     	return &XForwarded{
    -		insecure:   insecure,
    -		trustedIps: trustedIps,
    -		ipChecker:  ipChecker,
    -		next:       next,
    -		hostname:   hostname,
    +		insecure:          insecure,
    +		trustedIPs:        trustedIPs,
    +		connectionHeaders: connectionHeaders,
    +		ipChecker:         ipChecker,
    +		next:              next,
    +		hostname:          hostname,
     	}, nil
     }
     
    @@ -189,9 +194,53 @@ func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     
     	x.rewrite(r)
     
    +	x.removeConnectionHeaders(r)
    +
     	x.next.ServeHTTP(w, r)
     }
     
    +func (x *XForwarded) removeConnectionHeaders(req *http.Request) {
    +	var reqUpType string
    +	if httpguts.HeaderValuesContainsToken(req.Header[connection], upgrade) {
    +		reqUpType = unsafeHeader(req.Header).Get(upgrade)
    +	}
    +
    +	var connectionHopByHopHeaders []string
    +	for _, f := range req.Header[connection] {
    +		for _, sf := range strings.Split(f, ",") {
    +			if sf = textproto.TrimString(sf); sf != "" {
    +				// Connection header cannot dictate to remove X- headers managed by Traefik,
    +				// as per rfc7230 https://datatracker.ietf.org/doc/html/rfc7230#section-6.1,
    +				// A proxy or gateway MUST ... and then remove the Connection header field itself
    +				// (or replace it with the intermediary's own connection options for the forwarded message).
    +				if slices.Contains(xHeaders, sf) {
    +					continue
    +				}
    +
    +				// Keep headers allowed through the middleware chain.
    +				if slices.Contains(x.connectionHeaders, sf) {
    +					connectionHopByHopHeaders = append(connectionHopByHopHeaders, sf)
    +					continue
    +				}
    +
    +				// Apply Connection header option.
    +				req.Header.Del(sf)
    +			}
    +		}
    +	}
    +
    +	if reqUpType != "" {
    +		connectionHopByHopHeaders = append(connectionHopByHopHeaders, upgrade)
    +		unsafeHeader(req.Header).Set(upgrade, reqUpType)
    +	}
    +	if len(connectionHopByHopHeaders) > 0 {
    +		unsafeHeader(req.Header).Set(connection, strings.Join(connectionHopByHopHeaders, ","))
    +		return
    +	}
    +
    +	unsafeHeader(req.Header).Del(connection)
    +}
    +
     // unsafeHeader allows to manage Header values.
     // Must be used only when the header name is already a canonical key.
     type unsafeHeader map[string][]string
    
  • pkg/middlewares/forwardedheaders/forwarded_header_test.go+272 10 modified
    @@ -12,15 +12,16 @@ import (
     
     func TestServeHTTP(t *testing.T) {
     	testCases := []struct {
    -		desc            string
    -		insecure        bool
    -		trustedIps      []string
    -		incomingHeaders map[string][]string
    -		remoteAddr      string
    -		expectedHeaders map[string]string
    -		tls             bool
    -		websocket       bool
    -		host            string
    +		desc              string
    +		insecure          bool
    +		trustedIps        []string
    +		connectionHeaders []string
    +		incomingHeaders   map[string][]string
    +		remoteAddr        string
    +		expectedHeaders   map[string]string
    +		tls               bool
    +		websocket         bool
    +		host              string
     	}{
     		{
     			desc:            "all Empty",
    @@ -269,6 +270,196 @@ func TestServeHTTP(t *testing.T) {
     				xForwardedServer: "foo.com:8080",
     			},
     		},
    +		{
    +			desc:     "Untrusted: Connection header has no effect on X- forwarded headers",
    +			insecure: false,
    +			incomingHeaders: map[string][]string{
    +				connection: {
    +					xForwardedProto,
    +					xForwardedFor,
    +					xForwardedURI,
    +					xForwardedMethod,
    +					xForwardedHost,
    +					xForwardedPort,
    +					xForwardedTLSClientCert,
    +					xForwardedTLSClientCertInfo,
    +					xRealIP,
    +				},
    +				xForwardedProto:             {"foo"},
    +				xForwardedFor:               {"foo"},
    +				xForwardedURI:               {"foo"},
    +				xForwardedMethod:            {"foo"},
    +				xForwardedHost:              {"foo"},
    +				xForwardedPort:              {"foo"},
    +				xForwardedTLSClientCert:     {"foo"},
    +				xForwardedTLSClientCertInfo: {"foo"},
    +				xRealIP:                     {"foo"},
    +			},
    +			expectedHeaders: map[string]string{
    +				xForwardedProto:             "http",
    +				xForwardedFor:               "",
    +				xForwardedURI:               "",
    +				xForwardedMethod:            "",
    +				xForwardedHost:              "",
    +				xForwardedPort:              "80",
    +				xForwardedTLSClientCert:     "",
    +				xForwardedTLSClientCertInfo: "",
    +				xRealIP:                     "",
    +				connection:                  "",
    +			},
    +		},
    +		{
    +			desc:     "Trusted (insecure): Connection header has no effect on X- forwarded headers",
    +			insecure: true,
    +			incomingHeaders: map[string][]string{
    +				connection: {
    +					xForwardedProto,
    +					xForwardedFor,
    +					xForwardedURI,
    +					xForwardedMethod,
    +					xForwardedHost,
    +					xForwardedPort,
    +					xForwardedTLSClientCert,
    +					xForwardedTLSClientCertInfo,
    +					xRealIP,
    +				},
    +				xForwardedProto:             {"foo"},
    +				xForwardedFor:               {"foo"},
    +				xForwardedURI:               {"foo"},
    +				xForwardedMethod:            {"foo"},
    +				xForwardedHost:              {"foo"},
    +				xForwardedPort:              {"foo"},
    +				xForwardedTLSClientCert:     {"foo"},
    +				xForwardedTLSClientCertInfo: {"foo"},
    +				xRealIP:                     {"foo"},
    +			},
    +			expectedHeaders: map[string]string{
    +				xForwardedProto:             "foo",
    +				xForwardedFor:               "foo",
    +				xForwardedURI:               "foo",
    +				xForwardedMethod:            "foo",
    +				xForwardedHost:              "foo",
    +				xForwardedPort:              "foo",
    +				xForwardedTLSClientCert:     "foo",
    +				xForwardedTLSClientCertInfo: "foo",
    +				xRealIP:                     "foo",
    +				connection:                  "",
    +			},
    +		},
    +		{
    +			desc:     "Untrusted and Connection: Connection header has no effect on X- forwarded headers",
    +			insecure: false,
    +			connectionHeaders: []string{
    +				xForwardedProto,
    +				xForwardedFor,
    +				xForwardedURI,
    +				xForwardedMethod,
    +				xForwardedHost,
    +				xForwardedPort,
    +				xForwardedTLSClientCert,
    +				xForwardedTLSClientCertInfo,
    +				xRealIP,
    +			},
    +			incomingHeaders: map[string][]string{
    +				connection: {
    +					xForwardedProto,
    +					xForwardedFor,
    +					xForwardedURI,
    +					xForwardedMethod,
    +					xForwardedHost,
    +					xForwardedPort,
    +					xForwardedTLSClientCert,
    +					xForwardedTLSClientCertInfo,
    +					xRealIP,
    +				},
    +				xForwardedProto:             {"foo"},
    +				xForwardedFor:               {"foo"},
    +				xForwardedURI:               {"foo"},
    +				xForwardedMethod:            {"foo"},
    +				xForwardedHost:              {"foo"},
    +				xForwardedPort:              {"foo"},
    +				xForwardedTLSClientCert:     {"foo"},
    +				xForwardedTLSClientCertInfo: {"foo"},
    +				xRealIP:                     {"foo"},
    +			},
    +			expectedHeaders: map[string]string{
    +				xForwardedProto:             "http",
    +				xForwardedFor:               "",
    +				xForwardedURI:               "",
    +				xForwardedMethod:            "",
    +				xForwardedHost:              "",
    +				xForwardedPort:              "80",
    +				xForwardedTLSClientCert:     "",
    +				xForwardedTLSClientCertInfo: "",
    +				xRealIP:                     "",
    +				connection:                  "",
    +			},
    +		},
    +		{
    +			desc:     "Trusted (insecure) and Connection: Connection header has no effect on X- forwarded headers",
    +			insecure: true,
    +			connectionHeaders: []string{
    +				xForwardedProto,
    +				xForwardedFor,
    +				xForwardedURI,
    +				xForwardedMethod,
    +				xForwardedHost,
    +				xForwardedPort,
    +				xForwardedTLSClientCert,
    +				xForwardedTLSClientCertInfo,
    +				xRealIP,
    +			},
    +			incomingHeaders: map[string][]string{
    +				connection: {
    +					xForwardedProto,
    +					xForwardedFor,
    +					xForwardedURI,
    +					xForwardedMethod,
    +					xForwardedHost,
    +					xForwardedPort,
    +					xForwardedTLSClientCert,
    +					xForwardedTLSClientCertInfo,
    +					xRealIP,
    +				},
    +				xForwardedProto:             {"foo"},
    +				xForwardedFor:               {"foo"},
    +				xForwardedURI:               {"foo"},
    +				xForwardedMethod:            {"foo"},
    +				xForwardedHost:              {"foo"},
    +				xForwardedPort:              {"foo"},
    +				xForwardedTLSClientCert:     {"foo"},
    +				xForwardedTLSClientCertInfo: {"foo"},
    +				xRealIP:                     {"foo"},
    +			},
    +			expectedHeaders: map[string]string{
    +				xForwardedProto:             "foo",
    +				xForwardedFor:               "foo",
    +				xForwardedURI:               "foo",
    +				xForwardedMethod:            "foo",
    +				xForwardedHost:              "foo",
    +				xForwardedPort:              "foo",
    +				xForwardedTLSClientCert:     "foo",
    +				xForwardedTLSClientCertInfo: "foo",
    +				xRealIP:                     "foo",
    +				connection:                  "",
    +			},
    +		},
    +		{
    +			desc: "Connection: one remove, and one passthrough header",
    +			connectionHeaders: []string{
    +				"foo",
    +			},
    +			incomingHeaders: map[string][]string{
    +				connection: {
    +					"foo",
    +				},
    +				"Foo": {"bar"},
    +				"Bar": {"foo"},
    +			},
    +			expectedHeaders: map[string]string{
    +				"Bar": "foo",
    +			},
    +		},
     	}
     
     	for _, test := range testCases {
    @@ -299,7 +490,7 @@ func TestServeHTTP(t *testing.T) {
     				}
     			}
     
    -			m, err := NewXForwarded(test.insecure, test.trustedIps,
    +			m, err := NewXForwarded(test.insecure, test.trustedIps, test.connectionHeaders,
     				http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
     			require.NoError(t, err)
     
    @@ -382,3 +573,74 @@ func Test_isWebsocketRequest(t *testing.T) {
     		})
     	}
     }
    +
    +func TestConnection(t *testing.T) {
    +	testCases := []struct {
    +		desc              string
    +		reqHeaders        map[string]string
    +		connectionHeaders []string
    +		expected          http.Header
    +	}{
    +		{
    +			desc: "simple remove",
    +			reqHeaders: map[string]string{
    +				"Foo":      "bar",
    +				connection: "foo",
    +			},
    +			expected: http.Header{},
    +		},
    +		{
    +			desc: "remove and upgrade",
    +			reqHeaders: map[string]string{
    +				upgrade:    "test",
    +				"Foo":      "bar",
    +				connection: "upgrade,foo",
    +			},
    +			expected: http.Header{
    +				upgrade:    []string{"test"},
    +				connection: []string{"Upgrade"},
    +			},
    +		},
    +		{
    +			desc: "no remove",
    +			reqHeaders: map[string]string{
    +				"Foo":      "bar",
    +				connection: "fii",
    +			},
    +			expected: http.Header{
    +				"Foo": []string{"bar"},
    +			},
    +		},
    +		{
    +			desc: "no remove because connection header pass through",
    +			reqHeaders: map[string]string{
    +				"Foo":      "bar",
    +				connection: "Foo",
    +			},
    +			connectionHeaders: []string{"Foo"},
    +			expected: http.Header{
    +				"Foo":      []string{"bar"},
    +				connection: []string{"Foo"},
    +			},
    +		},
    +	}
    +
    +	for _, test := range testCases {
    +		t.Run(test.desc, func(t *testing.T) {
    +			t.Parallel()
    +
    +			forwarded, err := NewXForwarded(true, nil, test.connectionHeaders, nil)
    +			require.NoError(t, err)
    +
    +			req := httptest.NewRequest(http.MethodGet, "https://localhost", nil)
    +
    +			for k, v := range test.reqHeaders {
    +				req.Header.Set(k, v)
    +			}
    +
    +			forwarded.removeConnectionHeaders(req)
    +
    +			assert.Equal(t, test.expected, req.Header)
    +		})
    +	}
    +}
    
  • pkg/middlewares/headers/headers.go+2 4 modified
    @@ -10,7 +10,6 @@ import (
     	"github.com/traefik/traefik/v2/pkg/config/dynamic"
     	"github.com/traefik/traefik/v2/pkg/log"
     	"github.com/traefik/traefik/v2/pkg/middlewares"
    -	"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
     	"github.com/traefik/traefik/v2/pkg/tracing"
     )
     
    @@ -69,12 +68,11 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin
     
     	if hasCustomHeaders || hasCorsHeaders {
     		logger.Debugf("Setting up customHeaders/Cors from %v", cfg)
    -		h, err := NewHeader(nextHandler, cfg)
    +		var err error
    +		handler, err = NewHeader(nextHandler, cfg)
     		if err != nil {
     			return nil, err
     		}
    -
    -		handler = connectionheader.Remover(h)
     	}
     
     	return &headers{
    
  • pkg/server/server_entrypoint_tcp.go+1 0 modified
    @@ -565,6 +565,7 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati
     	handler, err = forwardedheaders.NewXForwarded(
     		configuration.ForwardedHeaders.Insecure,
     		configuration.ForwardedHeaders.TrustedIPs,
    +		configuration.ForwardedHeaders.Connection,
     		next)
     	if err != nil {
     		return nil, err
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.