Go 1.27 Drains HTTP/1 Bodies, but You Still Need Close

A Go client decodes the JSON and moves on, with a trailing newline still unread. When does Go 1.27 put that connection back in the pool, and when not?

Go 1.27 Drains HTTP/1 Bodies, but You Still Need Close
TLDR
  • Go 1.27 tries to recover HTTP/1 connections by draining unread response data after the body is closed.
  • The background drain is bounded by 50ms or 256KB; reuse is still conditional.
  • Keep closing response bodies, and reuse your Client and Transport.

Closing a body can now rescue the connection

Go 1.27 gives an HTTP/1 connection another chance when an application closes a response without consuming all of it. The Transport attempts a bounded background drain. Reaching the end can make that connection reusable.

The Go team's October 14 explanation describes a change shipped in the August release. It removes much of the reason for a manual drain helper, while leaving the caller responsible for closing the body.

Reading the result is not always reading every byte

An application may only need a status code, yet the server sends a body too. In HTTP/1, leaving those bytes unread gets in the way of locating the next response on the connection.

Nicholas Husin's implementation issue describes another case: a JSON decoder can finish an object while a trailing newline remains. The application has its answer, but the body has not necessarily reached EOF.

HTTP/2 has a different arrangement. Separate streams share the connection, so an unread body does not create this same obstacle to other requests.

The cleanup has a budget

The automatic drain is limited to 50ms or 256KB. Downloading a large or slowly delivered remainder can cost more than establishing another connection.

Response conditionExpected behavior
Unread HTTP/1 body closed by the callerA bounded drain attempts to preserve reuse.
Large or slow remainderCleanup does not continue without limits.
Connection: closeDraining is skipped because reuse is unavailable.

Because cleanup runs asynchronously, the next request is not guaranteed to get that connection. Once drained, it may serve a later request.

Keep Close, then check the connection pool

The package documentation still assigns body closure to the caller. It also recommends reusing Clients and Transports, which hold connection state. Removing a drain helper should not remove the close call beside it.

The release notes flag a less obvious case: programs that improperly retain excessive idle connections can become slower. They mention an unlimited MaxIdleConns setting of zero and different Clients used across requests. Inspect how your Transports and their pools are actually shared.

DisableKeepAlives disables reuse, but the notes recommend looking deeper at the configuration behind such regressions. Before simplifying cleanup code, establish which Go version runs it, where bodies are closed, and how the connection pool is managed.

Frequently asked questions

Can I omit Body.Close in Go 1.27?

No. The caller must still close the body. Closing triggers the bounded cleanup.

Does every HTTP workload become faster?

No. This addresses missed HTTP/1 reuse caused by unread response data. Do not assume the same benefit for workloads that already reuse connections or use HTTP/2.