# Go "x509: certificate relies on legacy Common Name field" fix

> Go 1.15+ rejects certificates that name the host only in the Common Name. Reissue the certificate with Subject Alternative Names; GODEBUG no longer helps.

Source: https://ssl.sh/errors/go-x509-certificate-relies-on-legacy-common-name/
Last updated: 2026-11-10

**`x509: certificate relies on legacy Common Name field, use SANs instead` means the server certificate names its host only in the Subject Common Name (CN) and has no Subject Alternative Name (SAN) extension, which Go no longer accepts.** The certificate must be reissued with SANs; there is no client-side switch left in current Go versions.

## Quick fix

Reissuing the certificate with SANs is the only fix.

Confirm that the certificate has no SAN extension:

```bash
openssl s_client -connect registry.internal.example.com:443 -servername registry.internal.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -ext subjectAltName
```

If you see `subject=CN = registry.internal.example.com` and no `X509v3 Subject Alternative Name` block, reissue the certificate with SANs. For an internal CA:

```bash
# 1. Key and CSR with SANs
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr \
  -subj "/CN=registry.internal.example.com" \
  -addext "subjectAltName=DNS:registry.internal.example.com,IP:203.0.113.10"

# 2. Sign it, copying the SAN into the certificate
cat > san.ext <<'EOF'
subjectAltName = DNS:registry.internal.example.com, IP:203.0.113.10
extendedKeyUsage = serverAuth
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 365 -sha256 -extfile san.ext -out server.crt
```

`openssl x509 -req` does **not** copy extensions from the CSR unless told to, which is how many SAN-less certificates were created; the `-extfile` above handles that.

## Causes

### 1. Old internal or self-signed certificate

Certificates made years ago with `openssl req -x509 -subj "/CN=host"` and no extensions are the typical source.

- **Confirm:** the [certificate decoder](https://ssl.sh/tools/certificate-decoder/) shows a CN but no SAN.
- **Fix:** reissue as above, or with the [CSR generator](https://ssl.sh/tools/csr-generator/) which always includes SANs.

### 2. Internal CA that drops SANs while signing

Some home-grown signing scripts and older CA software sign the CSR but ignore its extensions.

- **Confirm:** the CSR has SANs (`openssl req -in server.csr -noout -text`) but the issued certificate does not.
- **Fix:** configure the CA to copy or add `subjectAltName` (OpenSSL: `-extfile` or `copy_extensions = copy` in the CA config).

### 3. Appliances and embedded devices

Storage arrays, IPMI/BMC interfaces, older firewalls and printers often ship default certificates with only a CN. Replace the device certificate with one that has SANs, issued by your internal CA.

### 4. Upgraded client, unchanged server

The server did not change; your client was rebuilt with a newer Go version. That is why the error tends to appear after upgrading Docker, kubectl or Terraform.

## Fixes by environment

Every environment needs the same fix: reissue the certificate with the hostname in the SAN extension.

**Private Docker registry:** reissue the registry certificate with SANs, then restart the registry. Clients keep their CA in `/etc/docker/certs.d/<host>/ca.crt`; see [Docker x509 error](https://ssl.sh/errors/docker-x509-certificate-signed-by-unknown-authority/).

**Kubernetes admission webhooks:** the API server calls the webhook Service by its DNS name, for example `my-webhook.my-namespace.svc`. Put that name in the certificate's SANs. With cert-manager, list it in `spec.dnsNames` of the `Certificate`.

**etcd and kubeadm clusters:** kubeadm-generated certificates already include SANs. Hand-made etcd certificates from old tutorials may not; regenerate them with the node names and IPs as SANs.

**Java and other clients:** Java still falls back to the CN for DNS names in some cases, so a certificate may work in Java and fail in Go. Fix the certificate once and both work; see also [No subject alternative names present](https://ssl.sh/errors/java-no-subject-alternative-names-present/).

## Don't do this

- **Pinning an old Go version** or an old Docker/kubectl release to keep the fallback. You give up security fixes to keep a broken certificate.
- **`InsecureSkipVerify: true` or `--insecure-skip-tls-verify`.** This removes all verification, not only the CN check.
- **Setting `GODEBUG=x509ignoreCN=0` and assuming it worked.** On Go 1.17 and later the variable is ignored, so you will waste time on a no-op.
