Go 1.27 changes something that has sat in Go best-practice checklists for years: you no longer need to drain an HTTP/1 response body by hand before closing it to get the connection reused. The Go team explains it on their blog (October 14, 2026) with a benchmark that drops from ~710 ms to ~150 ms.
What changes
In HTTP/1, responses come back one after another on the same connection, so the client can only reuse it once the current body has been read to the end. Up to Go 1.26, if your code called resp.Body.Close() without reading everything, net/http threw the connection away and opened a new one, paying for TCP and TLS again. The article estimates about 3 RTT with TLS 1.2 (1 for TCP, 2 for TLS); with TLS 1.3 the handshake can finish in 1 RTT. HTTP/2 doesn't have this problem because it multiplexes streams.
In Go 1.27, net/http.Transport asynchronously drains an unread HTTP/1 body for up to 50 ms or 256 KB, so the connection can go back to the pool. If the response carries Connection: close, it skips draining because that would be wasted work.
The article's benchmark sends 100 sequential requests with HTTP/1 forced through GODEBUG=http2client=0. According to the authors (Nicholas Husin and Damien Neil) it takes ~710 ms on Go 1.26 and ~150 ms on Go 1.27, almost 5 times faster. It is one machine against one server with no variance data, so read it as an order of magnitude, not a promise.
Why it matters in production
The clients that feel it most speak HTTP/1 and send many requests in a row: calls to internal APIs behind a proxy that doesn't negotiate HTTP/2, health probes, scrapers, and service clients that leave bodies unread on error paths or on conditional HEAD and GET calls. Every discarded connection is extra latency and one more socket in TIME_WAIT, plus another TLS handshake on the server side.
It also cleans up code. The io.Copy(io.Discard, resp.Body) helpers with a size cap and a timeout that many projects carry around are no longer needed. The article simply recommends defer resp.Body.Close().
Two caveats from the text itself: the 256 KB cap exists because with large unread bodies a fresh connection is cheaper than draining, and the behavior "might evolve in future versions". It also documents no GODEBUG switch to turn it off, so if you worry about a side effect, you have to test it, not disable it. One more detail: a json.Decoder that leaves the trailing newline unread could break reuse in small cases too.
What I would do
- Measure before upgrading: on a service that makes many HTTP/1 calls, record new connections per second and client-side p99 latency (
httptraceor proxy/LB metrics). - Upgrade to Go 1.27 in a test environment and repeat the measurement with the same traffic.
- Find your "drain and close" helpers and check whether they still add anything. Remove them only if the numbers don't get worse.
- Review the places where a body is closed unread (error paths, redirects) and make sure a
Closeis always there. - Watch process CPU and memory: async draining moves work off the request path and into the background.
Sources
- Go blog, "HTTP/1 connection reuse" (Nicholas Husin and Damien Neil, October 14, 2026): https://go.dev/blog/http-connection-reuse