Bali, Indonesia
Insights

Wrong Certificate Served on a Shared IP? Test Each Hostname With openssl -servername

November 3, 2026
0:00
A domain on a shared IP suddenly shows a certificate warning, and when you click through, the certificate belongs to a completely different site on the same server. The certificate for the right domain is installed and valid, yet something else gets served. In most cases the server is doing exactly what it was told: it picked a certificate based on the hostname the client asked for, and either that hostname did not match any HTTPS virtual host or the client never sent a hostname at all. The good news is that you can prove which one it is in a few minutes with openssl s_client, without touching the server config. This guide walks through the checks, in the order a support engineer usually runs them.
When many sites share one IP address, the web server has a problem during the TLS handshake. The handshake happens before any HTTP request, so the server cannot read the Host header yet. It has to pick a certificate without knowing which site the visitor wants. SNI (Server Name Indication) solves this. It is a field in the first message of the handshake, the ClientHello, where the client says which hostname it is trying to reach. The server reads that name, finds the virtual host (vhost) configured for it on that IP and port, and sends that vhost's certificate. If no vhost matches the name, or the ClientHello contains no SNI at all, the server falls back to a default. In Apache that is typically the first matching VirtualHost for that IP and port. In Nginx it is the server block marked default_server, or the first one defined for that listen address. LiteSpeed and control panels handle this in their own way, so check how your setup defines the default. Whatever the rule, the result is the same: the visitor receives the certificate of whichever site happens to be the default, and the browser correctly complains that the name does not match. So a "wrong certificate" usually comes down to one of three things:
  1. The client sends SNI, but no HTTPS vhost on that IP matches the hostname (missing vhost, missing alias like www, or the vhost is bound to a different IP).
  2. The client does not send SNI, so it always gets the default certificate.
  3. DNS points the hostname at a different server or IP than the one you think, so you are looking at someone else's default.
The tests below separate these cases.
Always test against the exact IP the domain should live on, not just the hostname. That removes DNS from the equation for now. Check where the domain actually resolves with dig +short example.com first, and note the IP. The examples use 203.0.113.10, a documentation address; replace it and the hostnames with your own.
BASH
# 1) Ask for a specific hostname via SNI
echo | openssl s_client -connect 203.0.113.10:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# 2) Same IP, explicitly no SNI (what an old or misconfigured client sees)
echo | openssl s_client -connect 203.0.113.10:443 -noservername 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
The echo | closes the connection right after the handshake so the command does not sit waiting for input. Piping into openssl x509 trims the noisy handshake output down to the parts you care about: who the certificate is for (subject and the Subject Alternative Names), who issued it, and when it expires. One detail trips people up. Since OpenSSL 1.1.1, s_client sends SNI automatically when -connect uses a hostname. So openssl s_client -connect example.com:443 without -servername is not a no-SNI test on a modern system. Use -noservername to force it off, or connect to the IP literal without -servername, since SNI cannot carry an IP address. Older OpenSSL builds behave differently, and -noservername and x509 -ext need 1.1.1 or newer. Run openssl version if the output looks surprising. Now read the two results side by side:
  • With SNI shows the right certificate, without SNI shows another domain. The server is configured correctly. The default certificate simply belongs to another site, which is normal on a shared IP. If a real client still gets the wrong certificate, that client is not sending SNI. Look at the client: an outdated library, a monitoring check, or a script that connects by IP.
  • Both show the other domain's certificate. The server has no HTTPS vhost matching that hostname on this IP and port. This is a server-side configuration problem.
  • With SNI shows the right certificate for example.com but not for www.example.com. The bare domain has a vhost, but www is not listed as an alias, or the certificate's SAN list does not include it.
If you also want OpenSSL to check the name for you, add -verify_hostname example.com to the s_client command and look for the Verify return code line in the full output. A mismatch shows up there instead of you having to eyeball the subject.
Wrong-certificate reports rarely affect only one name. A missing alias or a vhost bound to the wrong IP often hits a group of domains at once, so it is worth sweeping all of them in one go.
BASH
# List every hostname that should be served from this IP
IP='203.0.113.10'
for h in example.com www.example.com shop.example.net mail.example.net; do
  printf '%-25s ' "$h"
  echo | openssl s_client -connect "IP":443 -servername "h" 2>/dev/null \
    | openssl x509 -noout -subject 2>/dev/null || echo 'no certificate returned'
done
Each line shows the hostname you asked for and the subject of the certificate the server actually returned. Any line where the two disagree is a hostname that fell through to the default vhost. If the subject looks right but the browser still complains, rerun that hostname with -ext subjectAltName, because modern clients check the SAN list, not the Common Name in the subject. The illustrative output format below shows what a mismatch looks like. The third line is the one to investigate.
TEXT
Illustrative output format:
example.com               subject=CN = example.com
www.example.com           subject=CN = example.com
shop.example.net          subject=CN = server1.hostingprovider.test
mail.example.net          subject=CN = mail.example.net
In this sample, shop.example.net is getting the server's default certificate. Either it has no SSL vhost on this IP, or the vhost exists but listens somewhere else.
Once openssl has shown you which hostnames fall through, look at how the web server sees its own vhosts. These commands only read configuration:
  • On Apache, apachectl -S (or httpd -S on some distros) prints every vhost grouped by IP and port, and marks which one is the default for each. If the problem hostname is not listed under the :443 section for the IP you tested, that is your answer.
  • On Nginx, nginx -T dumps the full loaded config. Search it for the hostname in server_name and check that the matching server block has listen 443 ssl on the right address. Also check which block carries default_server.
  • On LiteSpeed or a control panel managed server, the generated config layout depends on the setup. Use the panel's SSL or domain view to confirm the certificate is attached to the domain and to the IP the domain resolves to.
A common pattern is a vhost bound to a specific IP while DNS points the domain at a different one, for example after a migration or when a server has several IPs. Apache's VirtualHost 203.0.113.10:443 will not match requests arriving on another address, and the request lands in whatever vhost is default for that other address. Comparing the IP from dig +short with the IP in the vhost definition catches this quickly. Another pattern is a site that only ever had a port 80 vhost. HTTP works fine, so nobody notices until HTTPS is enabled or a redirect to HTTPS is added. If the domain sits behind a CDN or proxy, remember that dig returns the proxy's IP, and the certificate you see from the internet comes from the proxy. To test the origin itself, run the same openssl commands against the origin IP directly. curl -vI --resolve example.com:443:203.0.113.10 https://example.com is a useful cross-check too, since curl sends SNI for the hostname while connecting to the IP you specify. If curl and a browser disagree about the same certificate for other reasons, such as the chain, that is a different problem covered in Why curl Rejects an SSL Certificate That Works in Your Browser.
After you add the missing vhost, add the www alias, attach the certificate, or correct the bound IP, reload the web server the normal way for your setup and run the same loop again. Every hostname should now return its own certificate with SNI. The no-SNI test will still return the default certificate. That is expected on a shared IP, not a failure. If a specific client still gets the wrong certificate after the server checks out, the fix is on the client side. Update the library or tool, make sure it connects by hostname rather than IP, or, if it truly cannot send SNI, give that site a dedicated IP where its certificate is the default. The habit worth keeping is simple: when someone reports a wrong certificate, do not start by reinstalling it. Run openssl s_client against the real IP once with -servername and once with -noservername. The difference between those two answers tells you whether to look at the vhost config, the DNS, or the client, and the same commands confirm the fix afterwards.