Error message
curl: (60) SSL: no alternative certificate subject name matches target hostname 'api.example.com'
Also printed as
- curl: (60) SSL: no alternative certificate subject name matches target host name 'api.example.com'
curl 8.x prints "target hostname"; curl 7.x printed "target host name". The quoted host is whatever the URL contained. Very old curl versions (before 7.62.0) used exit code 51 for this check.
curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, hostname check in lib/vtls/openssl.c
What it means
The certificate chain is trusted, but none of the DNS names (or IP addresses) in the certificate's Subject Alternative Name extension match the host in the URL. curl compares the URL's host with the SAN entries only; the subject's Common Name is ignored when a SAN extension is present. The quoted name at the end is the host curl was asked to reach.
Why it happens
The certificate does not list this name
commonA certificate for example.com does not cover www.example.com or api.example.com unless each name is in the SAN list. New subdomains and new environments (staging, eu.) need a reissued certificate.
The URL uses an IP address, localhost or an internal alias
commonhttps://10.0.0.5, https://localhost or a docker-compose service name reach the right server but ask for a name the certificate was never issued for. IP addresses only match IP SAN entries, never DNS ones.
The server returned the certificate of another site
occasionalOn shared IPs the certificate is chosen by SNI. If the requested name has no matching server block, the server answers with its default certificate, which names a different site.
A wildcard is expected to cover more than it does
occasional*.example.com covers api.example.com but not example.com and not v2.api.example.com: a wildcard stands for exactly one label.
How to fix it
- 1.
Compare the SAN list with the URL
Print the subjectAltName extension of the served certificate. The fix is either a certificate that lists the name or a URL that uses one of the listed names.
bash # Names the served certificate actually covers echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \ | openssl x509 -noout -subject -ext subjectAltName - 2.
Reissue the certificate with every name in use
Request all hostnames (and IP addresses, if clients connect by IP) as SAN entries. Putting the name only into the Common Name does not work with any current client.
bash # ACME: list every name the certificate must cover sudo certbot certonly --nginx -d example.com -d www.example.com -d api.example.com # Private CA: put all names into subjectAltName (CN is ignored for matching) openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com" \ -addext "subjectAltName=DNS:api.example.com,DNS:www.example.com,IP:10.0.0.5" - 3.
Keep the name, change only the address
When you must reach a specific address (a load balancer node, a tunnel, a staging IP), --resolve or --connect-to keeps the URL's host, so SNI and the hostname check still use the certificate's name.
bash # Keep the certificate's name in the URL and steer the connection to another address curl --resolve api.example.com:443:10.0.0.5 https://api.example.com/ curl --connect-to api.example.com:443:lb-internal.example.net:443 https://api.example.com/ - 4.
Server owners: check SNI routing
If the certificate differs between requests with and without SNI, add a server block (or ingress rule) for the name and make sure the default certificate is not what clients get for real hostnames.
bash # Compare what the server returns with and without SNI openssl s_client -connect 10.0.0.5:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject openssl s_client -connect 10.0.0.5:443 -noservername </dev/null 2>/dev/null | openssl x509 -noout -subject
Related errors
x509: certificate is valid for example.com, not api.example.comGo crypto/x509Hostname/IP does not match certificate's altnames: Host: api.example.com. is no…Node.js tls.checkServerIdentity()[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch,…Python ssl module (OpenSSL host check)No subject alternative DNS name matching api.example.com found.Java (JSSE HostnameChecker)fatal: unable to access 'https://git.example.com/team/app.git/': SSL: certifica…git over HTTPS with libcurl-gnutls (Debian, Ubuntu)