<!-- https://pkizerotohero.com/errors/python-hostname-mismatch/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Fix Python's Hostname mismatch, certificate is not valid for '…': compare your hostname with the certificate's SANs and fix the name, SNI or certificate.

Source: https://pkizerotohero.com/errors/python-hostname-mismatch/ · Last reviewed: 2026-10-20

- Python
- Exact text checked with the real tool

# certificate verify failed: Hostname mismatch, certificate is not valid for '…'

Also seen in: requests, urllib, pip, httpx

**In one sentence:**

The certificate is trusted, but none of the names in its Subject Alternative Name list matches the hostname or IP address your code connected to.

[Check yours: Hostname matcher](https://pkizerotohero.com/tools/hostname/)

## Where you see it

In Python code that connects over HTTPS (`urllib`, `requests`, `pip`, `http.client`) after the chain has already been accepted. Since Python 3.7, OpenSSL does the name check during the handshake ([ssl module documentation](https://docs.python.org/3/library/ssl.html#ssl.SSLSocket.do_handshake)).

```text
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'api.internal.example.com'. (_ssl.c:1028)>
```

With `requests`:

```text
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.internal.example.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, "[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'api.internal.example.com'. (_ssl.c:1028)")))
```

If you connect by IP address, the wording changes:

```text
certificate verify failed: IP address mismatch, certificate is not valid for '10.0.0.5'. (_ssl.c:1028)
```

## What it means

A certificate lists the names it is valid for in its [Subject Alternative Name](https://pkizerotohero.com/handbook/glossary/#subject-alternative-name) (SAN) extension. Python compares the name in your URL with that list. The chain was trusted, so the certificate is genuine, but it was issued for other names. Python refuses it, because a valid certificate for another site proves nothing about this one.

## Likely causes

### You use a name the certificate does not list

Typical examples: a short internal name (`api` instead of `api.internal.example.com`), a load balancer’s own DNS name, `localhost`, or an old alias. See which names the certificate holds:

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

```text
subject=CN=www.example.com
X509v3 Subject Alternative Name: 
    DNS:www.example.com, DNS:example.com
```

**Fix:** connect with a name from that list, or have the certificate reissued with every name clients use. For a private CA, add the names to the request:

```bash
openssl req -new -key server.key -subj "/CN=api.internal.example.com" \
  -addext "subjectAltName=DNS:api.internal.example.com,DNS:api" -out server.csr
```

### You connect by IP address

An IP address matches only an IP entry in the SAN list (`IP Address:10.0.0.5`), never a DNS name. Use the DNS name, or reissue the certificate with an `IP:` entry if clients must use the address. Public CAs do not issue certificates for private (reserved) IP addresses.

### The server sends the wrong certificate

Servers that host several sites pick the certificate from the name the client sends in [SNI](https://pkizerotohero.com/handbook/glossary/#sni). If the name is unknown, the server falls back to a default certificate. Compare what you get with and without SNI:

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

If both show the default site’s names, add a `server_name api.internal.example.com;` block with the right certificate in nginx (or a matching `ServerName` virtual host in Apache, or a hostname binding in IIS).

### A wildcard does not cover this name

`*.example.com` covers exactly one label: `api.example.com`, but not `example.com` and not `v2.api.example.com`. Add the extra names as separate SAN entries.

### The name is only in the Common Name

If the certificate’s SAN list contains a DNS name, the Common Name is ignored. If it has no DNS name in its SAN list (no SAN at all, or only IP addresses), Python still falls back to the Common Name, but browsers and Go do not ([RFC 9525](https://www.rfc-editor.org/rfc/rfc9525) says clients should use the SAN). Reissue such certificates with a SAN.

Warning

Do not turn off the name check

`check_hostname = False`, `verify_mode = ssl.CERT_NONE` or `verify=False` in requests make the error go away by accepting a certificate for any site, including an attacker’s. Fix the name, the SNI configuration or the certificate instead.

## Check yours

*Interactive tool: Hostname matcher (runs in the browser at [/tools/hostname/](https://pkizerotohero.com/tools/hostname/))*

Paste the server’s certificate and type the hostname you connect to. The matcher shows which SAN entries match and why the others do not.

## Learn why

- [Names and jobs: SANs, wildcards, key usage and the CA flag](https://pkizerotohero.com/handbook/names-and-key-usage/)
- [The TLS 1.3 handshake](https://pkizerotohero.com/handbook/tls-handshake/): where SNI fits.
- The same problem elsewhere: [ERR\_TLS\_CERT\_ALTNAME\_INVALID](https://pkizerotohero.com/errors/node-err-tls-cert-altname-invalid/), [NET::ERR\_CERT\_COMMON\_NAME\_INVALID](https://pkizerotohero.com/errors/chrome-err-cert-common-name-invalid/).

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/errors/python-hostname-mismatch/).

Last reviewed 20 October 2026Edition 0.9

## Learn why it happens

- [Chapter 9: Names and jobs: SANs, wildcards, key usage and the CA flag](https://pkizerotohero.com/handbook/names-and-key-usage/)

## Fix it step by step

- [How to inspect a certificate with OpenSSL](https://pkizerotohero.com/guides/inspect-certificate-openssl/)
- [How to make Python, Node.js and npm trust a private CA](https://pkizerotohero.com/guides/trust-ca-python-node/)

## Everything on this topic

- [Certificates](https://pkizerotohero.com/topics/certificates/)
