From f9ba551501ba6730cb6eecc04e7919d01ab23d9d Mon Sep 17 00:00:00 2001 From: Clemens Hoffmann Date: Mon, 17 Aug 2026 13:51:55 +0200 Subject: [PATCH] Preserve all X-Forwarded-For header lines Fixes #580 --- src/code.cloudfoundry.org/gorouter/proxy/proxy.go | 7 ++++++- .../gorouter/proxy/proxy_test.go | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/code.cloudfoundry.org/gorouter/proxy/proxy.go b/src/code.cloudfoundry.org/gorouter/proxy/proxy.go index 6bc0e5392..60ae792d6 100644 --- a/src/code.cloudfoundry.org/gorouter/proxy/proxy.go +++ b/src/code.cloudfoundry.org/gorouter/proxy/proxy.go @@ -154,7 +154,12 @@ func NewProxy( r.Out.Header.Set("X-Forwarded-Host", host) } if clientIP, _, err := net.SplitHostPort(r.In.RemoteAddr); err == nil { - if prior := r.In.Header.Get("X-Forwarded-For"); prior != "" { + // Join ALL inbound X-Forwarded-For header lines, not just the + // first. A request can legitimately arrive with multiple + // X-Forwarded-For header lines (e.g. a proxy in front of gorouter + // appends its own line in addition to any the client/upstream + // already set). + if prior := strings.Join(r.In.Header.Values("X-Forwarded-For"), ", "); prior != "" { r.Out.Header.Set("X-Forwarded-For", prior+", "+clientIP) } else { r.Out.Header.Set("X-Forwarded-For", clientIP) diff --git a/src/code.cloudfoundry.org/gorouter/proxy/proxy_test.go b/src/code.cloudfoundry.org/gorouter/proxy/proxy_test.go index b5a703da4..2f7c0ac7e 100644 --- a/src/code.cloudfoundry.org/gorouter/proxy/proxy_test.go +++ b/src/code.cloudfoundry.org/gorouter/proxy/proxy_test.go @@ -496,6 +496,19 @@ var _ = Describe("Proxy", func() { Expect(getProxiedHeaders(req).Get("X-Forwarded-For")).To(Equal("1.2.3.4, 127.0.0.1")) }) }) + Context("when the header is already set as multiple separate lines", func() { + It("appends the client IP and preserves all prior values", func() { + // A request can arrive with X-Forwarded-For split across + // multiple header lines, e.g. when a proxy in front of + // gorouter appends its own line in addition to one the + // client or an upstream hop already set. All lines must be + // preserved. + req.Header.Add("X-Forwarded-For", "18.157.121.255") + req.Header.Add("X-Forwarded-For", "18.159.86.86") + Expect(getProxiedHeaders(req).Get("X-Forwarded-For")). + To(Equal("18.157.121.255, 18.159.86.86, 127.0.0.1")) + }) + }) }) Describe("X-Forwarded-Host", func() {