git.iko.soy/typeable/oauth2-proxy
on master help sign in
fix: WebSocket proxy to respect PassHostHeader setting (#3290)

* Fix WebSocket proxy to respect PassHostHeader setting When PassHostHeader is set to false, the regular HTTP proxy correctly sets the Host header to the upstream backend URL. However, the WebSocket proxy was not respecting this setting, causing WebSocket connections to fail when backend services validate the Host header. This commit: - Adds passHostHeader parameter to newWebSocketReverseProxy() - Applies setProxyUpstreamHostHeader() when PassHostHeader=false - Ensures consistent behavior between HTTP and WebSocket proxies Fixes #3288 Signed-off-by: Pascal Schmiel <pascal.schmiel@gmail.com> * chore(): add tests, update changelog Signed-off-by: Pascal Schmiel <pascal.schmiel@gmail.com> --------- Signed-off-by: Pascal Schmiel <pascal.schmiel@gmail.com>

commit1d6721f7ba94b5556cb7e07b93a31489a84bb7ec authorPascal <pascal.schmiel@gmail.com> dateFri 16 Jan 2026, 20:30 (9 months ago) parent3c22bc7
4 files changed+55−6
CHANGELOG.md+1 pkg/upstream/http.go+7−2 pkg/upstream/http_test.go+45−4 pkg/upstream/upstream_suite_test.go+2
CHANGELOG.md+1
@@ -8,6 +8,7 @@
88
99## Changes since v7.13.0
1010
11+- [#3290](https://github.com/oauth2-proxy/oauth2-proxy/pull/3290) fix: WebSocket proxy to respect PassHostHeader setting (@UnsignedLong)
1112- [#3197](https://github.com/oauth2-proxy/oauth2-proxy/pull/3197) fix: NewRemoteKeySet is not using DefaultHTTPClient (@rsrdesarrollo / @tuunit)
1213- [#3292](https://github.com/oauth2-proxy/oauth2-proxy/pull/3292) chore(deps): upgrade gomod and bump to golang v1.25.5 (@tuunit)
1314- [#3304](https://github.com/oauth2-proxy/oauth2-proxy/pull/3304) fix: added conditional so default is not always set and env vars are honored fixes 3303 (@pixeldrew)
pkg/upstream/http.go+7−2
@@ -55,7 +55,7 @@ func newHTTPUpstreamProxy(upstream options.Upstream, u *url.URL, sigData *option
5555 // Set up a WebSocket proxy if required
5656 var wsProxy http.Handler
5757 if ptr.Deref(upstream.ProxyWebSockets, options.DefaultUpstreamProxyWebSockets) {
58− wsProxy = newWebSocketReverseProxy(u, upstream.InsecureSkipTLSVerify)
58+ wsProxy = newWebSocketReverseProxy(u, upstream.InsecureSkipTLSVerify, upstream.PassHostHeader)
5959 }
6060
6161 var auth hmacauth.HmacAuth
@@ -201,7 +201,7 @@ func setProxyDirector(proxy *httputil.ReverseProxy) {
201201}
202202
203203// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
204−func newWebSocketReverseProxy(u *url.URL, skipTLSVerify *bool) http.Handler {
204+func newWebSocketReverseProxy(u *url.URL, skipTLSVerify *bool, passHostHeader *bool) http.Handler {
205205 wsProxy := httputil.NewSingleHostReverseProxy(u)
206206
207207 // Inherit default transport options from Go's stdlib
@@ -215,5 +215,10 @@ func newWebSocketReverseProxy(u *url.URL, skipTLSVerify *bool) http.Handler {
215215 // Apply the customized transport to our proxy before returning it
216216 wsProxy.Transport = transport
217217
218+ // Set upstream host header if PassHostHeader is false (same as regular HTTP proxy)
219+ if !ptr.Deref(passHostHeader, options.DefaultUpstreamPassHostHeader) {
220+ setProxyUpstreamHostHeader(wsProxy, u)
221+ }
222+
218223 return wsProxy
219224}
pkg/upstream/http_test.go+45−4
@@ -519,10 +519,11 @@ var _ = Describe("HTTP Upstream Suite", func() {
519519 Expect(websocket.Message.Send(ws, []byte(message))).To(Succeed())
520520 var response testWebSocketResponse
521521 Expect(websocket.JSON.Receive(ws, &response)).To(Succeed())
522− Expect(response).To(Equal(testWebSocketResponse{
523− Message: message,
524− Origin: origin,
525− }))
522+
523+ // When PassHostHeader=true (default), the Host should be the client's original request host
524+ Expect(response.Message).To(Equal(message))
525+ Expect(response.Origin).To(Equal(origin))
526+ Expect(response.Host).To(Equal(proxyURL.Host))
526527 })
527528
528529 It("will proxy HTTP requests", func() {
@@ -530,5 +531,45 @@ var _ = Describe("HTTP Upstream Suite", func() {
530531 Expect(err).ToNot(HaveOccurred())
531532 Expect(response.StatusCode).To(Equal(200))
532533 })
534+
535+ It("will proxy websockets respecting PassHostHeader=false", func() {
536+ // Create a new proxy server with PassHostHeader=false
537+ flush := 1 * time.Second
538+ timeout := options.DefaultUpstreamTimeout
539+ upstream := options.Upstream{
540+ ID: "websocketProxyNoPassHost",
541+ PassHostHeader: ptr.To(false),
542+ ProxyWebSockets: ptr.To(true),
543+ InsecureSkipTLSVerify: ptr.To(false),
544+ FlushInterval: &flush,
545+ Timeout: &timeout,
546+ }
547+
548+ u, err := url.Parse(serverAddr)
549+ Expect(err).ToNot(HaveOccurred())
550+
551+ handler := newHTTPUpstreamProxy(upstream, u, nil, nil)
552+ noPassHostServer := httptest.NewServer(middleware.NewScope(false, "X-Request-Id")(handler))
553+ defer noPassHostServer.Close()
554+
555+ origin := "http://example.localhost"
556+ message := "Hello, world!"
557+
558+ proxyURL, err := url.Parse(fmt.Sprintf("http://%s", noPassHostServer.Listener.Addr().String()))
559+ Expect(err).ToNot(HaveOccurred())
560+
561+ wsAddr := fmt.Sprintf("ws://%s/", proxyURL.Host)
562+ ws, err := websocket.Dial(wsAddr, "", origin)
563+ Expect(err).ToNot(HaveOccurred())
564+
565+ Expect(websocket.Message.Send(ws, []byte(message))).To(Succeed())
566+ var response testWebSocketResponse
567+ Expect(websocket.JSON.Receive(ws, &response)).To(Succeed())
568+
569+ // When PassHostHeader=false, the Host should be the upstream server address
570+ Expect(response.Host).To(Equal(u.Host))
571+ Expect(response.Message).To(Equal(message))
572+ Expect(response.Origin).To(Equal(origin))
573+ })
533574 })
534575})
pkg/upstream/upstream_suite_test.go+2
@@ -96,6 +96,7 @@ type testHTTPRequest struct {
9696type testWebSocketResponse struct {
9797 Message string
9898 Origin string
99+ Host string
99100}
100101
101102type testHTTPUpstream struct{}
@@ -138,6 +139,7 @@ func (t *testHTTPUpstream) websocketHandler() http.Handler {
138139 wsResponse := testWebSocketResponse{
139140 Message: string(data),
140141 Origin: ws.Request().Header.Get("Origin"),
142+ Host: ws.Request().Host,
141143 }
142144 err = websocket.JSON.Send(ws, wsResponse)
143145 if err != nil {