]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: strip request body headers on POST to GET redirects
authorSean Liao <sean@liao.dev>
Thu, 9 Oct 2025 01:26:02 +0000 (02:26 +0100)
committerGopher Robot <gobot@golang.org>
Fri, 10 Oct 2025 21:39:53 +0000 (14:39 -0700)
According to WHATWG Fetch, when the body is dropped in a redirect,
headers that describe the body should also be dropped.
https://fetch.spec.whatwg.org/#http-redirect-fetch

Fixes #57273

Change-Id: I84598f69608e95c1b556ea0ce5953ed43bf2d824
Reviewed-on: https://go-review.googlesource.com/c/go/+/710395
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/net/http/client.go
src/net/http/client_test.go

index 8faab2b17af01a2169669dfce371e401f8c34486..d6a801073553f759ea0120bc142a6aab8de4e5c3 100644 (file)
@@ -690,8 +690,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
                                        stripSensitiveHeaders = true
                                }
                        }
-                       copyHeaders(req, stripSensitiveHeaders)
-
+                       copyHeaders(req, stripSensitiveHeaders, !includeBody)
                        // Add the Referer header from the most recent
                        // request URL to the new one, if it's not https->http:
                        if ref := refererForURL(reqs[len(reqs)-1].URL, req.URL, req.Header.Get("Referer")); ref != "" {
@@ -758,7 +757,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) {
 // makeHeadersCopier makes a function that copies headers from the
 // initial Request, ireq. For every redirect, this function must be called
 // so that it can copy headers into the upcoming Request.
-func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensitiveHeaders bool) {
+func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensitiveHeaders, stripBodyHeaders bool) {
        // The headers to copy are from the very initial request.
        // We use a closured callback to keep a reference to these original headers.
        var (
@@ -772,7 +771,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensit
                }
        }
 
-       return func(req *Request, stripSensitiveHeaders bool) {
+       return func(req *Request, stripSensitiveHeaders, stripBodyHeaders bool) {
                // If Jar is present and there was some initial cookies provided
                // via the request header, then we may need to alter the initial
                // cookies as we follow redirects since each redirect may end up
@@ -810,12 +809,21 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensit
                // (at least the safe ones).
                for k, vv := range ireqhdr {
                        sensitive := false
+                       body := false
                        switch CanonicalHeaderKey(k) {
                        case "Authorization", "Www-Authenticate", "Cookie", "Cookie2",
                                "Proxy-Authorization", "Proxy-Authenticate":
                                sensitive = true
+
+                       case "Content-Encoding", "Content-Language", "Content-Location",
+                               "Content-Type":
+                               // Headers relating to the body which is removed for
+                               // POST to GET redirects
+                               // https://fetch.spec.whatwg.org/#http-redirect-fetch
+                               body = true
+
                        }
-                       if !(sensitive && stripSensitiveHeaders) {
+                       if !(sensitive && stripSensitiveHeaders) && !(body && stripBodyHeaders) {
                                req.Header[k] = vv
                        }
                }
index 2a3ee385f3c81071cac64dfb4f32f25e07c6d36e..d184f720319ce4fb9f4c9103564dcd577a1c9df9 100644 (file)
@@ -1621,6 +1621,39 @@ func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) {
        }
 }
 
+func TestClientStripHeadersOnPostToGetRedirect(t *testing.T) {
+       run(t, testClientStripHeadersOnPostToGetRedirect)
+}
+func testClientStripHeadersOnPostToGetRedirect(t *testing.T, mode testMode) {
+       ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
+               if r.Method == "POST" {
+                       Redirect(w, r, "/redirected", StatusFound)
+                       return
+               } else if r.Method != "GET" {
+                       t.Errorf("unexpected request method: %v", r.Method)
+                       return
+               }
+               for key, val := range r.Header {
+                       if strings.HasPrefix(key, "Content-") {
+                               t.Errorf("unexpected request body header after redirect: %v: %v", key, val)
+                       }
+               }
+       })).ts
+
+       c := ts.Client()
+
+       req, _ := NewRequest("POST", ts.URL, strings.NewReader("hello world"))
+       req.Header.Set("Content-Encoding", "a")
+       req.Header.Set("Content-Language", "b")
+       req.Header.Set("Content-Length", "c")
+       req.Header.Set("Content-Type", "d")
+       res, err := c.Do(req)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer res.Body.Close()
+}
+
 // Issue 22233: copy host when Client follows a relative redirect.
 func TestClientCopyHostOnRedirect(t *testing.T) { run(t, testClientCopyHostOnRedirect) }
 func testClientCopyHostOnRedirect(t *testing.T, mode testMode) {