On 8 October, Go 1.27.2 and 1.26.9 fixed 15 vulnerabilities in the standard library and the go command. Eight are in net/http, including two ways for a client to crash or exhaust an HTTP/2 server. Updating Go on your laptop fixes none of them in production. A Go binary carries the standard library it was built with, so every service, CLI and sidecar stays affected until someone rebuilds it with a fixed toolchain.
This guide is for teams that build or run Go software. It covers what the release fixes, which bugs matter for which programs, the five HTTP/2 bugs that also need a golang.org/x/net update, and a small scanner that reads build information out of binaries, so you can find what still needs a rebuild. We ran it on our own workstation: none of the 12 third-party Go tools installed there had been rebuilt.
The short answer
Go 1.27.2 and 1.26.9 (8 October 2026) fix 15 vulnerabilities: eight in net/http (five of them HTTP/2, plus HTTP/1 CONNECT handling and Range parsing), two in html/template, two checksum bypasses in the go command, and one each in net/textproto, crypto/tls and os on Windows. Standard library fixes only take effect when a binary is rebuilt, so rebuild every Go program with 1.27.2 or 1.26.9, starting with network servers. Programs that import golang.org/x/net/http2 directly also need golang.org/x/net v0.60.0. Go 1.25 and older get no fixes. Use go version -m or the scanner below to find binaries built with an older toolchain.
What the release fixes
| Area | CVE | What goes wrong | Reported by |
|---|---|---|---|
| HTTP/2 server | CVE-2026-97032 | A race in the HPACK encoder lets a client crash the server by changing the header table size while requests are in flight | RyotaK (GMO Flatt Security) |
| HTTP/2 server | CVE-2026-78659 | A Trailer header declaring many fields makes the server allocate memory past MaxHeaderBytes | RyotaK (GMO Flatt Security) |
| HTTP/2 server | CVE-2026-78669 | Repeated initial window size changes consume excessive CPU | Jakub Ciolek |
| HTTP/2 server | CVE-2026-78663 | Flow control credit refunded twice on server streams | Ali Sherif |
| HTTP/2 client | CVE-2026-78660 | Transport accepts malformed framing-related headers | TJ Barton |
| HTTP/1 server | CVE-2026-94439 | After a 2xx response to CONNECT, the server keeps reading HTTP from what should be a tunnel, which can enable request smuggling | Jakub Ciolek |
| HTTP/1 client | CVE-2026-56866 | A rejected CONNECT with a body leaves a pooled connection out of sync, so the next caller can read a response meant for an injected request | Rajat Raghav |
| HTTP | CVE-2026-78667 | Range headers with many small ranges burn CPU in FileServer, ServeContent and ServeFile | Jakub Ciolek |
| MIME | CVE-2026-94440 | Multipart parsing can read an arbitrarily long line past its memory limit | Jakub Ciolek |
| TLS | CVE-2026-97031 | Malformed ECH outer extension references cause memory amplification on servers | None listed |
| Templates | CVE-2026-94448, CVE-2026-97030 | Escaping errors in JavaScript contexts: consecutive template literal expressions, and the yield keyword | None listed |
go command | CVE-2026-94444, CVE-2026-94447 | A malicious project plus a malicious module proxy could bypass checksums for golang.org/fips140 and golang.org/toolchain | None listed |
os | CVE-2026-56857 | On Windows, Root.Mkdir and MkdirAll can follow a junction out of the root | Daniele Ballarini |
The HTTP/2 server bugs carry the most risk for most teams, because any client that can open a connection can trigger them. The Go HTTP server enables HTTP/2 automatically on TLS, so a service using ListenAndServeTLS or a TLS listener is exposed without doing anything special. Cleartext HTTP/2 is only on if you've enabled it, through Server.Protocols or the h2c package.
The two go command bugs are different in kind. They affect developer machines and CI, not running programs, and they need you to build a hostile project while using a module proxy the attacker controls. Updating the toolchain in CI images closes them.
Why updating Go isn't enough
Go links the standard library into each binary. A service built in September with Go 1.27.1 contains the September net/http, and it keeps that code until it's rebuilt, whatever is installed on the machine that runs it. The same goes for container images, release binaries downloaded from GitHub, and tools installed through a package manager.
Two details make this easy to miss:
- Dependency scanners don't see it. Lockfiles list modules, not the toolchain. A scanner reading
go.sumcan miss standard library CVEs entirely unless it also reads thegoortoolchainline, and that line is a minimum, not what built the binary. - The
godirective isn't the build version.go 1.27ingo.modsays what the code needs. Which release built the binary depends on the build machine or image, which is often pinned and forgotten.
Go writes the toolchain version into every binary it builds, though, and go version -m ./binary prints it along with the module list. That's what the scanner below automates.
The x/net half of the HTTP/2 fixes
Five of the HTTP/2 bugs (CVE-2026-97032, 78659, 78669, 78663 and 78660) also exist in golang.org/x/net/http2, the standalone package that the standard library's copy comes from. The fixed version is golang.org/x/net v0.60.0, published the same day. If your code imports golang.org/x/net/http2 or h2c directly, as gRPC-Go and many proxies do, you need both the new toolchain and the module update. If you only use net/http, the toolchain is enough.
Find binaries that need a rebuild
This program walks directories, reads the build information Go embeds in each binary, and checks the toolchain against the fixed releases. It also looks for function names that show whether HTTP server, HTTP/2, TLS server or template code is linked in. Go keeps function names in the binary even when the symbol table is stripped, so stripped builds work too. It flags golang.org/x/net below v0.60.0 when x/net/http2 is linked. It uses only the standard library:
// gobinscan finds Go binaries that need a rebuild for the 8 October 2026 Go
// security release (go1.27.2 / go1.26.9) or a golang.org/x/net bump (v0.60.0).
//
// Usage: go run . <dir-or-file>... (a directory is walked recursively)
package main
import (
"bytes"
"debug/buildinfo"
"fmt"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
)
// Function names stay in the binary's pclntab even when the symbol table is stripped.
var markers = map[string][]string{
"http-server": {"net/http.(*conn).serve"},
"http2": {"net/http/internal/http2.", "net/http.(*http2serverConn)", "golang.org/x/net/http2."},
"x/net-http2": {"golang.org/x/net/http2."},
"template": {"html/template.(*Template).Execute"},
"tls": {"crypto/tls.(*Conn).serverHandshake"},
}
// stdlibStatus compares a toolchain version like "go1.27.1" with the fixed releases.
func stdlibStatus(v string) string {
v = strings.Fields(v)[0]
parts := strings.Split(strings.TrimPrefix(v, "go"), ".")
if len(parts) < 2 {
return "unknown toolchain " + v
}
minor, _ := strconv.Atoi(parts[1])
patch := 0
if len(parts) > 2 {
patch, _ = strconv.Atoi(strings.SplitN(parts[2], "-", 2)[0])
}
switch {
case minor > 27:
return "ok"
case minor == 27 && patch >= 2, minor == 26 && patch >= 9:
return "ok"
case minor == 27, minor == 26:
return "REBUILD with go1.27.2 or go1.26.9"
default:
return "REBUILD: go1." + parts[1] + " gets no security fixes"
}
}
// older reports whether a v0.x.y module version is below v0.<major>.<minor>.0.
func older(v string, major, minor int) bool {
p := strings.Split(strings.TrimPrefix(v, "v"), ".")
if len(p) < 2 {
return false
}
a, _ := strconv.Atoi(p[0])
b, _ := strconv.Atoi(p[1])
return a < major || (a == major && b < minor)
}
func has(data []byte, keys []string) bool {
for _, k := range keys {
if bytes.Contains(data, []byte(k)) {
return true
}
}
return false
}
func scan(path string) {
info, err := buildinfo.ReadFile(path)
if err != nil {
return // not a Go binary
}
data, err := os.ReadFile(path)
if err != nil {
return
}
var uses []string
for _, k := range []string{"http-server", "http2", "template", "tls"} {
if has(data, markers[k]) {
uses = append(uses, k)
}
}
status := stdlibStatus(info.GoVersion)
if strings.HasPrefix(status, "REBUILD") && len(uses) == 0 {
status += " (low priority: no HTTP, TLS server or template code found)"
}
fmt.Printf("%s\n %s, main %s, code present: %s\n stdlib: %s\n", path, info.GoVersion,
info.Main.Path, strings.Join(uses, " "), status)
for _, d := range info.Deps {
if d.Replace != nil {
d = d.Replace
}
if d.Path == "golang.org/x/net" && older(d.Version, 0, 60) && has(data, markers["x/net-http2"]) {
fmt.Printf(" x/net: %s imports x/net/http2 directly, UPDATE to v0.60.0\n", d.Version)
}
}
}
func main() {
for _, root := range os.Args[1:] {
filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
if err == nil && d.Type().IsRegular() {
scan(p)
}
return nil
})
}
}
We built four test binaries with Go 1.27.1, which is affected, and with GOTOOLCHAIN=local so nothing else was downloaded: a web server using net/http and html/template, the same server built with -ldflags="-s -w", a small CLI, and an h2c server using golang.org/x/net v0.59.0. The folder also held a non-Go binary, which the scanner skips:
$ go run . ../fx/bin
../fx/bin/cli
go1.27.1, main example.com/cli, code present:
stdlib: REBUILD with go1.27.2 or go1.26.9 (low priority: no HTTP, TLS server or template code found)
../fx/bin/h2srv
go1.27.1, main example.com/h2srv, code present: http-server http2
stdlib: REBUILD with go1.27.2 or go1.26.9
x/net: v0.59.0 imports x/net/http2 directly, UPDATE to v0.60.0
../fx/bin/websrv
go1.27.1, main example.com/websrv, code present: http-server http2 template tls
stdlib: REBUILD with go1.27.2 or go1.26.9
../fx/bin/websrv-stripped
go1.27.1, main example.com/websrv, code present: http-server http2 template tls
stdlib: REBUILD with go1.27.2 or go1.26.9
"Code present" means the code is linked in, not that it's reachable. The web server contains HTTP/2 server code but serves plain HTTP/1, so the HTTP/2 bugs can't be triggered against it as written. It still needs a rebuild, and the scanner can't know how you'll deploy it. For a precise answer per binary, govulncheck -mode=binary checks which vulnerable functions are actually called.
Then we ran it, read-only, over the Homebrew and /usr/local/bin directories on one of our workstations. Leaving out the Go toolchain itself, it found 12 Go binaries from 12 different packages: five built with Go 1.27.1, six with 1.26.5 or 1.26.6, and one with Go 1.25.10, which no longer gets security fixes. None had been built with 1.27.2 or 1.26.9. Eight contained HTTP server code and nine linked x/net/http2 below v0.60.0. Most are command-line tools where that code serves a local callback or is never used, so the risk on a laptop is low. But the same pattern on a server image is not.
What to do
- Update build images and CI to Go 1.27.2 or 1.26.9. This also fixes the two
gocommand bugs on build machines. - Rebuild and redeploy Go services, starting with anything that terminates HTTP/2 or TLS from untrusted clients.
- Bump
golang.org/x/netto v0.60.0 in modules that importx/net/http2orh2cdirectly. - Move off Go 1.25 and older. Only the two newest major releases get security fixes.
- Scan what you ship, not just what you build: container images, downloaded release binaries and vendor tools. Run the scanner over an extracted image filesystem, or use
go version -m. - Ask vendors who ship Go binaries when they'll rebuild, and check the answer with the same tool.
For the long view, our Go standard library CVE trend report covers how often these releases come, and rebuilding Docker images covers making rebuilds routine rather than an event.
The fair view
The Go team's process is a model here. Fixes ship to both supported releases on the same day, each advisory goes into the Go vulnerability database with the affected packages and versions, and the HTTP/2 fixes land in x/net simultaneously. The build information that makes this audit possible is there because Go chose to embed it in every binary by default.
The cost is the other side of static linking. One go.mod change fixes a dependency for everyone who updates, but a standard library fix only reaches users through a rebuild of every program, by every publisher. Go releases these security updates roughly monthly. Teams that rebuild on a schedule absorb them without noticing. Teams that rebuild only when code changes can run a September net/http for a year.