Go 1.24.1 fixes IPv6 zone IDs in proxy exclusions
A zone identifier could make an IPv6 destination look like an excluded domain. Go's March security patch repairs host classification before proxy matching.
A proxy exclusion list gives a small piece of configuration considerable authority. A matching destination can bypass a proxy even when the process has a proxy configured for the request's scheme. If that match interprets part of an IP address as a domain name, a network policy can change without any change to its configuration.
Go 1.24.1, released on March 4, fixes such a classification error. The security announcement identifies CVE-2025-22870 in net/http and the external golang.org/x/net proxy packages. Go 1.23.7 carries the corresponding standard-library fix. The release also includes maintenance work in the compiler, cgo, and other packages, but proxy selection is the security issue requiring a closer explanation.
I favor Go's standard networking packages because common policy should be readable in one implementation. This defect is a useful example of how even a compact configuration interface depends on precise parsing. The application asks a simple question about proxy use, while the library must distinguish several kinds of host names and addresses before it can answer.
A suffix inside an address
The announcement describes an IPv6 URL with a zone ID containing .example.com. With NO_PROXY set to *.example.com, the affected matcher could treat that text as a hostname suffix and select a direct connection. The domain-shaped text was part of the zone identifier. It did not turn the IPv6 literal into a DNS name beneath example.com.
The security property depends on classifying the entire host before applying a rule for one category of host. Domain suffix matching makes sense for a DNS name. It is not an appropriate fallback merely because a different address parser failed to recognize the input. The bug allowed information from a distinct part of the address representation to influence a domain exclusion decision.
The public issue records the affected packages and the standard-library backports. That package list matters operationally. Programs using the standard HTTP transport obtain its implementation through the Go distribution. Programs using the external proxy packages also have a module dependency to consider. A language toolchain version and a selected external module version identify different copies of related code.
The x/net regression test uses a non-loopback IPv6 example. It configures NoProxy as example.com, sets a proxy, and checks http://[1000::%25.example.com]:123. The expected result is the configured proxy URL. This choice avoids confusing the domain-matching defect with the implementation's independent treatment of loopback addresses.
Classification precedes matching
The Go 1.24.1 vendored implementation shows the repaired decision path. It separates host and port, recognizes localhost, and parses the host with netip.ParseAddr. When parsing succeeds, it converts the address bytes into the representation used by the IP matchers. The resulting non-nil IP value also tells the domain matcher that this input is an address.
That last detail is important. The domain matcher immediately rejects inputs for which the IP argument is non-nil. Successful address classification therefore does two jobs. It enables address-specific exclusion rules and prevents domain-specific rules from claiming the same input. The fix changes the interpretation on which later policy depends.
Loopback handling remains explicit. A recognized loopback IP bypasses the proxy under the implementation's existing special case. Consequently, the patch should not be described as forcing every zone-bearing address through a proxy. Its purpose is to prevent a zone identifier from creating an unrelated domain match. Whether another valid exclusion applies remains a separate decision.
The same source documents how exclusion entries work. A domain without a leading dot matches itself and its subdomains. With a leading dot, a domain matches subdomains only. Using a standalone asterisk disables proxying for all destinations. These are substantial policy differences despite their short textual forms. None authorizes treating a suffix in an IPv6 zone identifier as a domain.
The environment is an input to routing
FromEnvironment reads HTTP_PROXY, HTTPS_PROXY, and NO_PROXY, with lowercase alternatives. Configuration then selects a proxy according to the URL scheme and applies exclusions. Proxy selection returns no proxy when there is no applicable proxy configured, as well as when a valid exclusion applies. Identical results can therefore arise from different policy paths.
This distinction affects diagnosis. Observing a direct connection does not establish whether the proxy configuration was absent or whether an exclusion matched. The interesting evidence is the parsed destination and the rule that selected the path. For this bug, a destination that remained an IPv6 address nevertheless reached domain matching as though it were a hostname.
The library also constructs a proxy-selection function from a copy of the configuration. Its documentation says that later changes to the original configuration do not alter functions already created from it. That is a useful contract for callers that build policy once and reuse it. It also means a caller cannot assume that editing a configuration object changes an already-created routing decision function.
No extra application syntax is necessary to receive the standard-library correction. The changed parsing and matching happen below normal HTTP requests. Applications with explicit custom transport or proxy logic need to assess that code according to its own behavior, since the release cannot change a caller's independent implementation.
What the patch says about network policy
Proxy configuration often looks like string processing because its inputs are strings. The actual decision concerns typed identities. An address, a domain, and an optional port each have different matching rules. A parser's failure needs an explicit meaning, especially when a later fallback can grant an exception to normal routing.
I consider that ordering part of the policy itself. A domain exclusion is only as narrow as the classification that decides which inputs are domains. Go 1.24.1 makes zone-bearing IPv6 hosts participate in address handling and prevents their zone text from satisfying domain exclusions. The regression test establishes the resulting routing decision for a concrete destination without making an external network connection necessary to explain it.