Hey Gophers,

If you only need the status code from an HTTP response, do you still need to read the body? In older Go versions, skipping it could mean opening a new connection for the next request. Go 1.27 makes that easier, and we’ll walk through a small example below.

First, a few reads on updating an API field, a surprising bug when retrying HTTP requests, and two tools that save some manual work.

Warm-up Time: Golang Nugget

Warm-up Time

Go team · 8 October 2026

If you’re using Go 1.27 or 1.26, there are new patch releases to pick up. They fix security issues in the Go command and packages including net/http, crypto/tls, and html/template, along with compiler and runtime bugs. You can stay on your current Go branch: update to 1.27.2 or 1.26.9, depending on which one you use.

Redowan Delowar · 12 October 2026

Suppose your API lets someone edit their profile. Sending a new address should replace the old one. Leaving address out should keep it unchanged. But you also need a way to clear it. With a *string in your Go request struct, a missing field and a JSON null can both become nil, so the server can’t tell the two intentions apart.

Redowan shows another approach: the client sends a list of fields to change, called a field mask. For example, ?update_mask=address tells the server to update the address, even if the new value is empty. Leave address out of that list, and the stored address stays as it is. The article builds a small Go handler around this idea.

Jesús Espino · 6 October 2026

You send a request, get an error response, and retry. It seems safe to reuse the request body at that point. But a server can reject a request before Go has finished sending all of it, so the first attempt may still be reading from that body.

VictoriaMetrics found this in vmauth, its HTTP proxy. The retry shared the first attempt’s reader, including the position it had reached in the data. Both attempts could move that position, causing the retry to skip or repeat parts of the body. Giving each attempt its own reader fixed the bug, while allowing them to share the unchanged bytes.

Jesús follows the problem from the race-detector warning through Go’s HTTP client. He also explains why the team chose to keep a different race in another component, where the affected bytes would never be used by the server.

Tool Time: Golang Nugget

Tool Time

Hongxiang Jiang · Go blog · 16 October 2026

gopls is the Go language server behind many of your editor’s Go features. This post shows how it can handle edits that need a choice from you first. For example, select a struct, choose to add tags, then tell it you want json tags with snake_case names. It adds them across the struct, so you don’t have to edit every field yourself.

It can also generate the empty methods your type needs to implement an interface, ready for you to fill in. The post shows how to try these actions with gopls 0.24 and the VS Code Go extension 0.56.

Anton Medvedev · 5 October 2026

fx is a terminal tool for browsing and filtering JSON. If an API gives you a large response, you can open it in fx and explore the fields without leaving the terminal. Version 40 lets you write a query inside the viewer and see the result change as you type. Tab completes field names from the data, which helps when you don’t know its structure yet.

It also adds support for formats including XML and HTML. If you already use fx, one key has changed: Escape now cancels a query; q quits the viewer.

Deep Dive: Golang Nugget

Deep Dive

A status check that opens too many connections

Imagine a service that checks another service’s status. The endpoint returns 200 OK and a small diagnostic body, but the caller only needs the status code.

Here’s the whole check. The caller passes in an HTTP client and a request. Watch what happens to the body after we get a response:

func status(client *http.Client, req *http.Request) (int, error) {
    resp, err := client.Do(req)
    if err != nil {
        return 0, err
    }
    defer resp.Body.Close()
    return resp.StatusCode, nil
}

The defer closes the body when the function returns. We take the status code and leave the body unread.

An HTTP connection can carry more than one request, which saves the work of opening a new one each time. But with HTTP/1.1, Go needs to finish reading one response before it can use that connection for the next request. Otherwise, some bytes from the old response would still be waiting to be read.

Through Go 1.26, closing an unread body generally meant closing the connection too. Our status check worked, but the next check had to open another connection.

Go now tries to finish the cleanup

In Go 1.27, Go’s HTTP client tries to read and discard the remaining HTTP/1 body after you close it. This cleanup is often called draining. It happens in the background, so your function can return while Go finishes reading. If the cleanup succeeds, the connection is kept ready for another request.

The Go team’s explanation recommends keeping the simple Close call above instead of writing a helper to drain the body yourself.

Go won’t spend unlimited time on that cleanup. The current implementation allows up to 256 KiB of unread data and 50 ms to finish reading it. A response can be too large or too slow, in which case Go gives up on reusing that connection.

See what changes on the second request

To check this, the example uses a local server that returns a 1 KiB body. The client makes two requests. Go’s HTTP tracing package tells us whether the second one reused an existing connection or opened a new one.

Here are the results, including two variations: reading the body ourselves, and returning a much larger body.

What happens to the first response

Go 1.26.1

Go 1.27.2

Close an unread 1 KiB body

New connection

Reused

Read the 1 KiB body, then close it

Reused

Reused

Close an unread 512 KiB body

New connection

New connection

The first row is the change we’re interested in. The same code closes the body without reading it, but Go 1.27.2 manages to keep the connection. The last row shows the limit: a 512 KiB body is too large for this cleanup, so both versions open a new connection.

There is one detail in the test: it waits for Go to report that the connection is ready for reuse, with a timeout if that never happens. That gives the background cleanup a chance to finish. If your application sends its next request immediately, Go may still need to open another connection.

This example uses HTTP/1.1 without TLS and checks whether a connection is reused. It doesn’t measure a speed improvement. HTTP/2 handles responses on separate streams, so it doesn’t have this same unread-body problem.

For the status check above, the code can stay simple: reuse your HTTP client, set a suitable timeout, and keep closing the response body. Go 1.27 can now do more of the cleanup for you. The client documentation still requires that Close call.

Watch: building HTTP clients you can change

GopherCon 2022 · recording uploaded 26 December 2023 · older talk

Once several parts of an application make HTTP calls, testing and changing those clients can get awkward. Tom’s talk covers decorators: small wrappers that add behavior around a client. He uses them to make clients easier to extend and test. It’s an older talk about client design and doesn’t cover Go 1.27’s new cleanup behavior.

Did the connection example make sense? Hit reply and tell me whether your client reads the whole response or usually stops at the status code.

Thanks for reading!
Moein

Reply

Avatar

or to participate