Go Blog: Go 1.27 Drains Unread HTTP/1 Response Bodies on Close, Cutting a 100-Request Benchmark From About 710 ms to About 150 ms
A Go blog post says Go 1.27's net/http now drains unread HTTP/1 response bodies for up to 50 ms or 256 KB on close, so connections can be reused.
Overview
Go 1.27 changes how the standard library’s HTTP/1 client handles response bodies that a program closes without reading to the end. In a post dated 14 October 2026 on the Go Blog, authors Nicholas Husin and Damien Neil write that a 100-call benchmark against index.golang.org “takes ~710 milliseconds on Go 1.26 on my machine. On Go 1.27, the same program takes ~150 milliseconds.” The Go 1.27 release notes list the change under net/http. Go 1.27 was previously reported as a stable release.
What Changed
The Go 1.27 release notes state: “HTTP/1 Response.Body now automatically drains any unread content upon being closed, up to a conservative limit, to allow better connection reuse. For most programs, this change should be a no-op, or result in a performance improvement.”
According to the Go Blog, net/http.Transport “will asynchronously drain an unread HTTP/1 response body for up to 50 ms or 256 KB.” The post also says the transport implements additional optimizations; when a response contains a Connection: close header, draining is skipped because it would be wasted work.
Why Connections Were Being Abandoned
The Go Blog explains that HTTP/1 responses on one connection are sent back-to-back, so the client can find the start of the next response only after the current body has been read to the end. Through Go 1.26, the post says, when resp.Body.Close was called on a body that still held unread data, net/http “abandons the existing connection and creates new connections for its next requests.”
The post describes the cost of that behavior in round trips. Setting up a new connection takes a TCP handshake of 1 round-trip time (RTT) and, with TLS 1.2, a TLS handshake of 2 RTT, so HTTP requests can be sent only after a total of 3 RTT, according to the Go Blog. Its benchmark target, index.golang.org, supports TLS 1.3, which the authors say lets the TLS handshake finish in 1 RTT, yet the difference was still noticeable.
The authors identify two common triggers. A client may care only about a status code while the server sends a body that goes unread. Programs that do read the body can also be affected: the post says encoding/json.Decoder stops reading at the closing brace of a JSON object, so a trailing newline sent by the server stays unread, and the connection cannot be reused until that newline is consumed. The Go Blog states that HTTP/2 does not have this problem because it multiplexes streams over a single connection.
The Manual Workaround It Replaces
The post says developers have long drained bodies themselves before closing them. It walks through a version of that pattern that caps the drain at 256 KB and 50 milliseconds and runs in the background, noting that if the unread body is large, “establishing a new TCP connection can be faster than downloading and discarding the rest of the body.” Go 1.27’s Transport, per the same post, “automatically does everything we have implemented in the code above.”
The authors now recommend that users “simply close their HTTP bodies once they no longer need them,” and conclude: “Let your code stay clean and simple, and let net/http deal with the complexities on your behalf.”
What to Watch
The release notes flag a rare downside. According to the Go 1.27 release notes, programs that do not benefit from connection reuse “might experience performance degradation if they had been improperly allowing an excessive amount of idle connections to linger,” usually by setting Transport.MaxIdleConns to 0 or by using different Clients for different requests. For those cases, the notes say, setting Transport.DisableKeepAlives to true will disable connection reuse, and they add that such degradation “usually indicates improper configuration or usage of Transport or Client in the first place.”
What We Don’t Know
The roughly 5x figure comes from a single-machine measurement the authors describe as run on their own machine, against one public host and with HTTP/2 disabled through GODEBUG=http2client=0. The post does not publish results for other servers, network conditions or workloads. It also says the drain behavior “might evolve in future versions of Go, as other possible enhancements become apparent.” The sources do not say how many existing programs are affected.