Skip to content

Go 1.27 fixes an HTTP/1 bug that silently killed connection reuse

An unread response body used to force a fresh TCP connection on every request. Go's net/http.Transport now drains it automatically, and the Go team's own benchmark shows requests running nearly five times faster.

Source: The Go Blog

Go’s standard library has had a quiet performance trap in net/http.Transport: if an HTTP/1 response body isn’t read to completion before Close(), the connection can’t be reused for the next request, because HTTP/1 serves responses sequentially on one stream. Code that only checks a status code, or a JSON decoder that stops right after the closing brace, leaves bytes unread and forces a new connection and TLS handshake every time.

Go 1.27 changes net/http.Transport to drain an unread HTTP/1 response body automatically and asynchronously, up to 50 milliseconds or 256KB, before closing it, skipping the drain when the response carries Connection: close. In the Go team’s own benchmark, calling a simple GET against index.golang.org 100 times took about 710 milliseconds on Go 1.26 versus about 150 milliseconds on Go 1.27, with no code changes required.

resp, err := http.Get(url)
if err != nil {
    return err
}
defer resp.Body.Close() // connection now reused even if resp.Body is never read

Why it matters: this is one of the most common accidental performance bugs in Go HTTP clients, and the fix needs no code changes, just upgrading. Anyone who added manual io.Copy(io.Discard, resp.Body) workarounds can drop them.

The caveat: the drain only covers bodies up to 256KB or 50ms of reading; a client that streams a large or slow body without reading it still loses the connection.

Share

Related reading

More from The Wire

All briefs

GitHub Copilot retires six AI models today

Gemini 3.7 Flash, GPT-5.5, GPT-5.4, GPT-5.4 mini, GPT-5 mini and Grok 4.5 stop working across Copilot Chat, inline edits, agent mode and completions from today.

via GitHub