Consider this function, which sends a GET request to a URL and returns the HTTP status code. Using HTTP/1 (GODEBUG=http2client=0), calling it 100 times against index.golang.org takes about 710 milliseconds on Go 1.26 on the author's machine. On Go 1.27, the same program takes about 150 milliseconds, almost 5 times faster.
The gap comes from the cost of establishing connections. Making a request over a new connection requires a TCP handshake (1 round-trip time, or RTT), a TLS handshake (2 RTT under TLS 1.2), and then the HTTP request itself, for a total of about 3 RTT. In cross-region communication, one-way latencies often reach or exceed 50ms, so setup alone can cost around 300 ms. Even with index.golang.org, which uses TLS 1.3 and offers relatively low latency, opening a new connection remains very noticeable.
To avoid this cost, HTTP clients and servers try to reuse connections. HTTP/1.0 clients and servers opt in with a Connection: keep-alive header, while HTTP/1.1 connections are persistent by default. HTTP/2 multiplexes multiple requests and responses over a single TCP connection.
Reuse can fail in HTTP/1, however. Responses on the same connection are sent sequentially, so net/http can only locate the start of the next response once the current response body has been read to the end. In the example, the response body is never read, so when resp.Body.Close is called, net/http finds an unread body and cannot safely reuse the connection. Through Go 1.26, this forces a new connection for each request.
Source: Hacker News · Summarized by HeadlinesBriefing