Introduction
Server Name Indication (SNI) is a TLS extension that allows a server to present different SSL certificates for different hostnames on the same IP address. Clients that do not support SNI (Windows XP, Java 6, old Android, legacy embedded systems) cannot indicate which hostname they are connecting to during the TLS handshake. The server returns its default certificate, which likely does not match the requested hostname, causing a certificate name mismatch error.
Symptoms
- Old client connects but gets the wrong certificate (for a different domain on the same server)
- Browser shows certificate name mismatch for the requested hostname
openssl s_client -connect server:443works (it supports SNI) but the application does not- Java 6 application fails with
java.security.cert.CertificateException: No name matching - Windows XP client shows certificate for
default.cominstead oftarget.com
Common Causes
- Client does not support the TLS SNI extension (pre-OpenSSL 0.9.8f, Java 6, old Windows)
- Multiple SSL virtual hosts sharing one IP address without SNI support
- Server's default SSL certificate does not match the client's target hostname
- Embedded IoT devices with outdated TLS libraries
- Legacy load balancer terminating SSL without SNI support
Step-by-Step Fix
- 1.Test SNI support from the client:
- 2.```bash
- 3.# Without SNI (should get the default certificate)
- 4.openssl s_client -connect server:443 </dev/null 2>/dev/null | \
- 5.openssl x509 -noout -subject
- 6.# With SNI (should get the correct certificate)
- 7.openssl s_client -connect server:443 -servername target.com </dev/null 2>/dev/null | \
- 8.openssl x509 -noout -subject
- 9.
` - 10.Option A: Assign a dedicated IP address for the non-SNI client's domain:
- 11.```nginx
- 12.# Each IP address hosts one SSL certificate
- 13.server {
- 14.listen 192.168.1.100:443 ssl;
- 15.server_name legacy-app.example.com;
- 16.ssl_certificate /etc/nginx/ssl/legacy-app.pem;
- 17.ssl_certificate_key /etc/nginx/ssl/legacy-app.key;
- 18.}
- 19.
` - 20.Option B: Use a wildcard or multi-SAN certificate as the default:
- 21.```nginx
- 22.server {
- 23.listen 443 ssl default_server;
- 24.# Use a certificate that covers all hosted domains
- 25.ssl_certificate /etc/nginx/ssl/wildcard-example-com.pem;
- 26.ssl_certificate_key /etc/nginx/ssl/wildcard-example-com.key;
- 27.}
- 28.
` - 29.Option C: Configure a fallback certificate covering common hostnames:
- 30.```nginx
- 31.server {
- 32.listen 443 ssl default_server;
- 33.ssl_certificate /etc/nginx/ssl/fallback.pem; # SAN: site1.com, site2.com, site3.com
- 34.ssl_certificate_key /etc/nginx/ssl/fallback.key;
- 35.}
- 36.
` - 37.For Java clients, enable SNI explicitly:
- 38.```bash
- 39.# Java 7 supports SNI but may need to enable it
- 40.export JAVA_OPTS="-Djsse.enableSNIExtension=true"
- 41.# For Java 6, SNI is not available - upgrade Java or use dedicated IP
- 42.
` - 43.Verify the fix for non-SNI clients:
- 44.```bash
- 45.# Simulate a non-SNI client by not sending servername
- 46.openssl s_client -connect server:443 </dev/null 2>/dev/null | \
- 47.openssl x509 -noout -subject -issuer
- 48.# Should now show a certificate that covers the target hostname
- 49.
`
Prevention
- Use wildcard certificates or multi-SAN certificates as the default server certificate
- Assign dedicated IP addresses for services accessed by legacy clients
- Document SNI requirements and client compatibility in infrastructure documentation
- Test all new SSL deployments against non-SNI clients if legacy access is required
- Plan IP address allocation to accommodate dedicated IPs for non-SNI clients