Visitar URL original
net/http: Connection header values lost when close is included · Issue #82065 · golang/go · GitHub
Skip to content

net/http: Connection header values lost when close is included #82065

Open

Description

@zamyatinmn

Go version

go version go1.27.1 windows/amd64

Output of go env in your module/workspace:

set AR=ar
set CC=gcc
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_ENABLED=0
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set CXX=g++
set GCCGO=gccgo
set GO111MODULE=
set GOAMD64=v1
set GOARCH=amd64
set GOAUTH=netrc
set GOBIN=C:\Users\Maksim\go\bin
set GOCACHE=C:\Users\Maksim\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\Maksim\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\Maksim\AppData\Local\Temp\go-build1384164418=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=NUL
set GOMODCACHE=C:\Users\Maksim\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPACKAGESDRIVER=
set GOPATH=C:\Users\Maksim\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:\Users\Maksim\AndroidStudioProjects\muzlogate-p1\.tools\go
set GOSUMDB=sum.golang.org
set GOTELEMETRY=local
set GOTELEMETRYDIR=C:\Users\Maksim\AppData\Roaming\go\telemetry
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=C:\Users\Maksim\AndroidStudioProjects\muzlogate-p1\.tools\go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.27.1
set GOWORK=
set PKG_CONFIG=pkg-config

What did you do?

Run this standalone reproducer with an unmodified Go toolchain:

package main

import (
    "bufio"
    "fmt"
    "io"
    "net"
    "net/http"
    "net/http/httptest"
    "net/http/httputil"
    "net/url"
    "time"
)

func main() {
    ln, err := net.Listen("tcp", "127.0.0.1:0")
    if err != nil { panic(err) }
    defer ln.Close()
    go func() {
        c, err := ln.Accept()
        if err != nil { return }
        defer c.Close()
        c.SetDeadline(time.Now().Add(5 * time.Second))
        if _, err := http.ReadRequest(bufio.NewReader(c)); err != nil { return }
        io.WriteString(c, "HTTP/1.1 200 OK\r\nConnection: X-Hop, close\r\nX-Hop: connection-only\r\nX-Extension: keep\r\nContent-Length: 2\r\n\r\nok")
    }()
    target, err := url.Parse("http://" + ln.Addr().String())
    if err != nil { panic(err) }
    p := httputil.NewSingleHostReverseProxy(target)
    tr := http.DefaultTransport.(*http.Transport).Clone()
    p.Transport = tr
    defer tr.CloseIdleConnections()
    front := httptest.NewServer(p)
    defer front.Close()
    front.Client().Timeout = 5 * time.Second
    res, err := front.Client().Get(front.URL)
    if err != nil { panic(err) }
    defer res.Body.Close()
    body, err := io.ReadAll(res.Body)
    if err != nil { panic(err) }
    fmt.Printf("status=%d X-Hop=%q X-Extension=%q body=%q\n", res.StatusCode, res.Header.Get("X-Hop"), res.Header.Get("X-Extension"), body)
}

What did you see happen?

The reverse proxy forwards the connection-specific X-Hop header.

status=200 X-Hop="connection-only" X-Extension="keep" body="ok"

Reproduced on Go 1.26.8, 1.27.1, and master (Windows/amd64).

What did you expect to see?

The reverse proxy should remove X-Hop because it is nominated by Connection, while preserving X-Extension.

status=200 X-Hop="" X-Extension="keep" body="ok"

RFC 9110 §7.6.1 requires intermediaries to remove connection-specific fields before forwarding.

Root cause: readTransfer/shouldClose removes the entire Connection field when it contains close. ReverseProxy subsequently cannot determine which other fields were nominated.

Related: #44034 (historical parser behavior), #81319 (1xx responses), #80875 (Upgrade).

A local candidate fix exists, but it changes observable Response.Header and Response.Write behavior. Maintainer guidance on compatibility would be appreciated before submitting a CL.

Activity

  1. gopherbot commented on Oct 8, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/846726 mentions this issue: net/http: preserve response Connection options for intermediaries

  2. changed the title [-]net/http: ReadResponse drops Connection options needed by ReverseProxy[/-] [+]net/http: Connection header values lost when close is included[/+] on Oct 11, 2026
  3. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions