ecdsa.com
Go crypto/tls (Go ≥ 1.20)Go

Error message

tls: failed to verify certificate: x509: certificate signed by unknown authority

Go crypto/tls (Go ≥ 1.20) — wrapping x509 verification errors

What it means

Since Go 1.20, TLS handshake failures wrap the underlying x509 error with a "tls: failed to verify certificate:" prefix, so logs show both the layer (TLS handshake) and the root cause (chain building failed). The suffix varies — unknown authority, expired, hostname mismatch — and is the part to act on. The prefix is also a handy log filter: grepping for it separates TLS trust failures from application-level errors, which is exactly why the wrapping was added.

Why it happens

How to fix it

  1. 1.

    Give the client the right trust anchors

    Same remedy as the bare x509 error: a root pool containing the issuing CA, set on the transport actually used for the dial.

    go
    pool := x509.NewCertPool()
    caPEM, _ := os.ReadFile("ca.pem")
    pool.AppendCertsFromPEM(caPEM)
    
    client := &http.Client{Transport: &http.Transport{
        TLSClientConfig: &tls.Config{RootCAs: pool},
    }}
    resp, err := client.Get("https://internal.example:8443/healthz")
  2. 2.

    Inspect what the server presents

    See the served chain, its issuers and validity before touching client code — most fixes turn out to be server-side chain configuration.

    bash
    openssl s_client -connect internal.example:8443 -servername internal.example \
      </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
  3. 3.

    Keep verification on, even in tests

    httptest servers hand you a pre-configured client; use it instead of disabling verification globally.

    go
    srv := httptest.NewTLSServer(handler)
    defer srv.Close()
    resp, err := srv.Client().Get(srv.URL)   // trusts srv's cert, nothing else

Related errors

← Browse the full signature error database