I was debugging why HTTPS requests to app.example.com were failing with certificate errors. The certificate on the server was valid, but clients were getting “certificate name mismatch” errors. Turns out the server hosted multiple domains on the same IP, and I was getting the wrong certificate. SNI solved it.

What is SNI? Link to heading

Server Name Indication (SNI) is a TLS extension that lets a server host multiple SSL certificates on a single IP address. The client sends the hostname in the initial TLS handshake, and the server responds with the correct certificate.

Without SNI, the server just sends its “default” certificate, which might not match the domain you’re requesting.

Basic connection test Link to heading

openssl s_client -connect example.com:443

This works for simple cases, but if the server uses SNI (most do nowadays), you might get the wrong certificate.

With SNI (the right way) Link to heading

openssl s_client -connect example.com:443 -servername example.com

The -servername flag is crucial for SNI. Without it, you might get the wrong certificate and wonder why your domain doesn’t work.

Real debugging scenario Link to heading

Here’s what happened to me:

I was setting up a new Ingress in Kubernetes for api.example.com. The certificate was issued by cert-manager and looked correct in the secret. But curl was failing:

curl https://api.example.com
curl: (60) SSL certificate problem: certificate subject name mismatch

First, I checked if the certificate existed on the server:

echo | openssl s_client -connect api.example.com:443 2>/dev/null | openssl x509 -noout -subject

Output:

subject= /CN=default.example.com

Wrong certificate! The server was sending the default certificate instead of the one for api.example.com.

Then I tested with SNI:

echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | openssl x509 -noout -subject

Output:

subject= /CN=api.example.com

Correct certificate! This confirmed the certificate existed but curl wasn’t using SNI properly. Turns out the client was an old version of curl without SNI support. Upgrading curl fixed it.

Useful commands Link to heading

Show the full certificate chain:

openssl s_client -connect example.com:443 -servername example.com -showcerts

This dumps all certificates in the chain. Useful for debugging intermediate certificate issues.

Check certificate expiry:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

Output:

notBefore=Dec  1 00:00:00 2025 GMT
notAfter=Feb 28 23:59:59 2026 GMT

Check the Subject Alternative Names (SANs):

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep -A 1 "Subject Alternative Name"

This shows which domains the certificate is valid for.

Verify the certificate chain:

openssl s_client -connect example.com:443 -servername example.com -verify_return_error

If the chain is invalid, this command exits with an error.

Quiet mode (just show errors):

openssl s_client -connect example.com:443 -servername example.com -quiet

Test against a specific IP (useful when DNS isn’t set up yet):

openssl s_client -connect 1.2.3.4:443 -servername app.example.com

This connects to the IP but sends the SNI hostname. Great for testing before changing DNS.

Common certificate issues Link to heading

1. Certificate name mismatch Link to heading

Error: certificate subject name mismatch

Cause: The certificate doesn’t include the domain you’re connecting to.

Fix: Check the SANs on the certificate:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep DNS:

If your domain isn’t listed, the certificate is wrong.

2. Expired certificate Link to heading

Error: certificate has expired

Cause: Certificate validity period has passed.

Fix: Check the dates:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

Renew the certificate if it’s expired.

3. Self-signed certificate Link to heading

Error: self signed certificate in certificate chain

Cause: Certificate is self-signed or signed by an untrusted CA.

Fix: Either trust the CA, or replace the certificate with one from a trusted CA (like Let’s Encrypt).

For testing, you can skip verification:

curl -k https://example.com

But never do this in production.

4. Intermediate certificate missing Link to heading

Error: unable to get local issuer certificate

Cause: Server isn’t sending the full certificate chain.

Fix: Check the chain:

openssl s_client -connect example.com:443 -servername example.com -showcerts

You should see multiple certificates. If you only see one, the intermediate certificate is missing. Configure the server to include it.

5. Wrong certificate on SNI server Link to heading

Error: Certificate looks wrong or is for a different domain.

Cause: You’re not using -servername.

Fix: Always use -servername when testing domains with SNI:

openssl s_client -connect example.com:443 -servername example.com

Testing tools Link to heading

These tools are easier to use than raw OpenSSL for some tasks:

  • SSL Labs Server Test: Comprehensive SSL/TLS testing. Shows certificate details, protocol support, vulnerabilities. Recommended for all public-facing domains.

  • testssl.sh: Command-line tool for testing SSL/TLS. More comprehensive than OpenSSL s_client.

testssl.sh https://example.com
  • sslscan: Another CLI tool for SSL/TLS testing.
sslscan example.com:443
curl -v https://example.com 2>&1 | grep -A 10 "SSL connection"

Quick reference Link to heading

# Test with SNI
openssl s_client -connect example.com:443 -servername example.com

# Check expiry
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

# Check subject
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject

# Check SANs
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep DNS:

# Test against IP with SNI
openssl s_client -connect 1.2.3.4:443 -servername example.com

# Show full chain
openssl s_client -connect example.com:443 -servername example.com -showcerts

The echo | at the start sends an empty input so the command exits after connecting. Otherwise s_client waits for input.

Resources Link to heading