Programming languages
Go "x509: certificate is valid for X, not Y": how to fix
x509: certificate is valid for www.example.com, not example.comx509: certificate is valid for www.example.com, not example.com means the server's certificate covers the names before "not", and the host you connected to, after "not", is not one of them. Connect with one of the listed names, send the right ServerName, or get a certificate that includes your host. InsecureSkipVerify is not a fix.
What the error means
The host you dialed is missing from the certificate's Subject Alternative Names. When a Go TLS client verifies the server, it checks the host from the URL (or tls.Config.ServerName) against the certificate's Subject Alternative Names. On a mismatch, crypto/x509 returns a HostnameError, and since Go 1.20 the TLS layer wraps it in a CertificateVerificationError. With net/http the full line looks like:
Get "https://example.com/": tls: failed to verify certificate: x509: certificate is valid for www.example.com, not example.com
The HostnameError text varies with the situation, straight from Go's verify.go:
x509: certificate is valid for A, B, not C: DNS SANs exist but none matches.x509: certificate is valid for 203.0.113.5, not 203.0.113.10: you dialed an IP and the IP SANs differ.x509: cannot validate certificate for 203.0.113.10 because it doesn't contain any IP SANs: you dialed an IP; the certificate has no IP SANs.x509: certificate is not valid for any names, but wanted to match example.com: no SANs at all.x509: certificate relies on legacy Common Name field, use SANs instead: the name is only in the CN; see legacy Common Name.
On Linux, where Go verifies certificates itself, the host name is checked before the chain is built. A self-signed default certificate from a proxy or ingress therefore often surfaces as this name error rather than as signed by unknown authority.
Causes, most common first
A URL with a name the certificate does not cover is the most common cause.
1. The URL uses a name the certificate does not cover
Bare domain versus www, an old alias, or a wildcard used too deep (*.example.com matches api.example.com, not example.com or v1.api.example.com).
- Confirm:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
- Fix: use a listed name, or reissue the certificate with every name you serve.
2. A proxy or ingress returns its default certificate
ingress-nginx serves a self-signed fallback certificate for ingress.local when no TLS secret matches; other load balancers and CDNs return their own default certificate for unknown hosts. The error then reads valid for ingress.local, not example.com or names the provider's domain.
- Confirm:
kubectl get ingress -A -o wideand check that the Ingress has atlsentry with your host and an existing secret. - Fix: add the TLS section and secret, or upload the certificate to the provider.
3. Dialing an IP or a different host without ServerName
Custom dialers, service discovery returning IPs, or http.Transport configured to connect to a fixed address all verify against whatever host Go thinks it is talking to. Without ServerName, Go sends no SNI for an IP, so a multi-site server may return the wrong certificate too.
- Fix: set
ServerName(code below), or call the service by a DNS name that is on the certificate.
4. Internal services with incomplete SANs
Private CAs often issue certificates for api.internal while clients call api.internal.example.com, a Kubernetes service name or a Docker container name. Issue the certificate with every name and IP clients use.
5. A different server answered
If the names in the error belong to an unrelated site, DNS, /etc/hosts, a captive portal or a filtering proxy sent you elsewhere. Check nslookup example.com and compare with where the service should live.
Fix commands by platform
Every fix makes the name the client verifies match a SAN in the certificate.
Any OS, test a specific IP with the correct name (SNI and verification):
curl -v --resolve example.com:443:203.0.113.10 https://example.com/
Go, dial an IP but verify the real host name:
tr := &http.Transport{
TLSClientConfig: &tls.Config{ServerName: "api.example.com"},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://203.0.113.10/health")
Kubernetes ingress-nginx:
spec:
tls:
- hosts: ["example.com", "www.example.com"]
secretName: example-com-tls
rules:
- host: example.com
Windows: Go on Windows uses the platform verifier for chains, but the name rules are the same; check the certificate with certutil -dump cert.cer or the certificate decoder.
Don't disable certificate verification
tls.Config{InsecureSkipVerify: true} makes Go accept any certificate and any host name. The Go documentation states that in this mode TLS is susceptible to machine-in-the-middle attacks unless custom verification is used, and that it should be used only for testing or together with VerifyConnection or VerifyPeerCertificate. In production that means anyone on the path can impersonate your API or registry and read credentials.
Safer alternatives: correct the host name, set ServerName, fix the ingress or CDN certificate, issue certificates with complete SANs, and for private CAs add the root to RootCAs or the system pool (SSL_CERT_FILE / SSL_CERT_DIR are honoured on Unix).
Verify the fix
conn, err := tls.Dial("tcp", "example.com:443", &tls.Config{})
if err != nil { log.Fatal(err) }
fmt.Println(conn.ConnectionState().PeerCertificates[0].DNSNames)
conn.Close()
The dial must succeed without error and print a list that includes the host you use. Check the public view with the SSL checker, and read about Subject Alternative Names when planning a certificate's name list.
FAQ
Why does Go reject a certificate that works in older tools?
Go only matches host names against the Subject Alternative Name extension. Since Go 1.15 the legacy Common Name is ignored, so a certificate that only has the name in its CN fails; that case has its own message, covered in certificate relies on legacy Common Name.
The error names ingress.local. What is that?
It is the host name on the self-signed fallback certificate that ingress-nginx serves when no TLS secret matches the request. The Ingress has no tls section for your host, the secret name is wrong, or the host in the request differs from the one in the Ingress rule. Fix the Ingress and the error disappears.
How do I call an IP address but verify the real host name?
Set tls.Config{ServerName: "api.example.com"} on the transport. Go then sends api.example.com as SNI and checks the certificate against that name, while the TCP connection still goes to the IP you dialed.
go mod download fails with certificate is valid for ..., not proxy.golang.org. Why?
Something other than the real proxy answered: a captive portal, a filtering proxy or a DNS answer pointing elsewhere. Check nslookup proxy.golang.org and the certificate subject. On networks where the default proxy is unreachable, set GOPROXY to a reachable module proxy instead of disabling checks.