tailscale/tailscale · error
no certificate for hostname %q; this server only serves IP a
Error message
no certificate for hostname %q; this server only serves IP address certificates
What it means
ipCertManager.getCertificate received a TLS ClientHello whose SNI (hi.ServerName) is a DNS name, but this manager only issues certificates for the server's own IP addresses, and no downstream hostname provider is chained (m.nextTLS is nil or has no GetCertificate). The handshake is aborted, so clients see a TLS alert instead of a certificate.
Source
Thrown at cmd/derper/ipcert.go:183
}
ta, ok := hi.Conn.LocalAddr().(*net.TCPAddr)
if !ok {
return netip.Addr{}, false
}
ip := ta.AddrPort().Addr().Unmap()
return ip, ip.IsValid()
}
func (m *ipCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
connIP, connIPOK := connLocalIP(hi)
if hi.ServerName != "" {
sniIP, err := netip.ParseAddr(hi.ServerName)
if err != nil {
// The SNI is a DNS name; let the hostname provider handle it.
if m.nextTLS != nil && m.nextTLS.GetCertificate != nil {
return m.nextTLS.GetCertificate(hi)
}
return nil, fmt.Errorf("no certificate for hostname %q; this server only serves IP address certificates", hi.ServerName)
}
if !connIPOK || sniIP.Unmap() != connIP {
return nil, fmt.Errorf("requested certificate for IP %v does not match the connection's IP address", sniIP)
}
}
if !connIPOK {
return nil, errors.New("unable to determine the connection's local IP address")
}
ctx := hi.Context()
if ctx == nil {
ctx = context.Background()
}
return m.certForIP(ctx, connIP)
}
// certForIP returns the current certificate for ip, obtaining one
// first if there is no unexpired certificate for it. Concurrent
// callers for the same IP share a single issuance.View on GitHub (pinned to cfe32b8be6)
Solutions
- Run derper with a DNS-name cert mode as well (e.g. --hostname plus ACME), so ipCertManager chains to the hostname provider via next.
- Point clients at the IP address (DERPMap node with IP, and TLS SNI set to the IP string) instead of a DNS name.
- If you must keep the DNS name, move the derper to a host where it can get a hostname certificate and disable IP-only mode.
Example fix
# before: only IP certs; DNS SNI handshakes fail derper --certdir=/var/lib/derper --stun ... (IP cert mode only) # after: chain a hostname cert provider behind the IP cert manager derper --hostname=derp.example.com --certmode=letsencrypt ... # DNS names now served by the next provider
Defensive patterns
Strategy: validation
Validate before calling
// Client side: only send an SNI you know the server can serve.
host := "203.0.113.10"
if net.ParseIP(host) == nil && ipOnlyServer {
host = serverIP // use the IP literal instead of a DNS name
}
conn, err := tls.Dial("tcp", host+":443", &tls.Config{ServerName: host}) Type guard
func isIPLiteral(s string) bool { return netip.ParseAddr(s).IsValid() } // valid SNI for an IP-cert derper Try / catch
cert, err := mgr.GetCertificate(hi)
if err != nil && strings.Contains(err.Error(), "only serves IP address certificates") {
// route the connection to a hostname-capable provider or reject early
return nil, err
} Prevention
- When serving only IP certs, give clients the raw IP (DERPMap nodes accept IPs).
- If a DNS name must work, also configure a hostname cert provider so nextTLS delegation exists.
- Health checks should either skip SNI or use the IP string.
When it happens
Trigger: Running derper in IP-address cert mode as the top-level certProvider while clients reach it via a DNS hostname: the client puts that hostname in SNI, netip.ParseAddr fails (it is not an IP literal), and there is no next provider to delegate to. Typical when a DNS A record points at the derper IP but the config only enables IP certs.
Common situations: Adding a friendly DNS name to a derper deployed with only IP cert mode; health checks or monitoring probes that send Hostname/SNI; tailscale DERPMap entries configured with a DNS hostname while the server serves IP certs.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- cert mismatch with hostname: %q
- failed to create tls.Certificate: %v
- ACME account key: %w
- invalid PEM in %s
- requested certificate for IP %v does not match the connectio
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/1a1b47267a9305b0.
Report an issue: GitHub.