Skip to content

proposal: net: resolve localhost names internally #78485

Description

@dgl

Go version

go1.25.6

Output of go env in your module/workspace:

AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/dgl/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/dgl/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3561637139=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='openbsd'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/home/dgl/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='openbsd'
GOPATH='/home/dgl/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/dgl/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/local/go/pkg/tool/openbsd_amd64'
GOVCS=''
GOVERSION='go1.25.6'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

In RFC 6761 section 6.3 "localhost" and names falling within ".localhost"
https://datatracker.ietf.org/doc/html/rfc6761#section-6.3 it says in part 3:

  3.  Name resolution APIs and libraries SHOULD recognize localhost
       names as special and SHOULD always return the IP loopback address
       for address queries and negative responses for all other query
       types.  Name resolution APIs SHOULD NOT send queries for
       localhost names to their configured caching DNS server(s).

The macOS and OpenBSD resolvers do this by default, various other platforms vary in their exact implementation.

This can be demonstrated fairly easily:

package main

import (
  "log"
  "net"
)

func main() {
  log.Print(net.LookupHost("foo.localhost"))
}

What did you see happen?

$ GODEBUG=netdns=go+1 ./dnstest
go package net: GODEBUG setting forcing use of the Go resolver
2026/04/01 12:09:14 [] lookup foo.localhost on 8.8.8.8:53: no such host
$ GODEBUG=netdns=cgo+1 ./dnstest
go package net: GODEBUG setting forcing use of the cgo resolver
2026/04/01 12:09:45 [127.0.0.1 ::1] <nil>

What did you expect to see?

Always resolve foo.localhost. Note that 8.8.8.8 does not resolve foo.localhost but for example 9.9.9.9 does, so there are two workarounds for this, either force the cgo resolver as above, or use quad9 or another recursive resolver which does implement the other parts of RFC 6761:

$ grep '^nameserver' /etc/resolv.conf
nameserver 9.9.9.9
$ GODEBUG=netdns=go+1 ./dnstest
go package net: GODEBUG setting forcing use of the Go resolver
2026/04/01 12:10:49 [::1 127.0.0.1] <nil>

I would also expect that this resolution happens internally to the Go dns resolver code, so that a query for localhost or foo.localhost is never seen on the wire (localhost may be covered by the usual /etc/hosts entry, but subdomains are not).

Metadata

Metadata

Assignees

No one assigned

    Labels

    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Proposal

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions