# SNI (Server Name Indication)

> SNI is a TLS extension that sends the hostname in the ClientHello so one IP can serve many certificates. Learn debugging with curl and nginx upstream pitfalls.

Source: https://howhttpworks.com/glossary/sni
Last reviewed: 2026-10-04

> **TL;DR:** SNI puts the hostname in the first, unencrypted TLS message so a server hosting many sites on one IP can present the right certificate. `Host` is the HTTP-level equivalent, but it arrives too late for that job.

Server Name Indication (SNI) is a TLS extension, defined in RFC 6066, that lets a client include the hostname it is trying to reach in its ClientHello. Servers and load balancers use it to select the certificate and often the backend before any HTTP is exchanged. Without it, one IP address and port could serve only one certificate.

## Seeing it with curl

```bash
curl -v https://www.example.com/
```

curl sends `www.example.com` as SNI automatically. To test a specific IP while keeping both SNI and `Host` correct, use `--resolve`:

```bash
curl -v --resolve www.example.com:443:203.0.113.10 https://www.example.com/
```

Writing `curl https://203.0.113.10/ -H 'Host: www.example.com'` is different: SNI is the IP (or absent), so the server may present its default certificate, and you get a name mismatch.

## Non-obvious facts

- **SNI and `Host` can disagree.** A client can send one name in SNI and another in `Host`. Many servers answer from the `Host` value after choosing a certificate from SNI. For HTTP/2 reuse across hostnames, the server may reply `421 Misdirected Request` to say "wrong server for this name".
- **nginx does not send SNI to upstreams by default.** For `proxy_pass https://backend`, you need `proxy_ssl_server_name on;` (and usually `proxy_ssl_name`) or an SNI-based backend fails the handshake. In the nginx error log this appears as `SSL_do_handshake() failed ... while SSL handshaking to upstream`, and clients see a 502.
- **It leaks the destination.** Even with HTTPS, the SNI hostname is visible to networks and middleboxes, which is how many filters work. Encrypted Client Hello is the fix, but it is not universal.
- **Name mismatch errors come from here.** Chrome's `ERR_CERT_COMMON_NAME_INVALID` often means the server returned its default certificate because the request carried the wrong SNI, or none.
- **IP addresses are not sent as SNI.** RFC 6066 says literal IP addresses are not permitted in the `server_name` field.

## Go deeper

- [HTTPS and TLS guide](https://howhttpworks.com/guides/https-and-tls)
- [TLS handshake](https://howhttpworks.com/glossary/tls-handshake)
- [Host header](https://howhttpworks.com/headers/host)
- [421 Misdirected Request](https://howhttpworks.com/status-codes/421)
- [Certificate common name error](https://howhttpworks.com/debug/err-cert-common-name-invalid)
