---
title: Is my SSL certificate valid? How to check it with the SSL Certificate Check
description: The browser says "Your connection is not private" on one hostname and works on the next. To check an SSL certificate, connect to the server the way a browser does and read what it actually serves: who issued it, the dates, and every name it covers. Here's how to do it in seconds with the free SSL Certificate Check, how to read a wildcard SAN list, and the openssl commands that confirm it.
canonical: https://watchfor.io/blog/how-to-check-an-ssl-certificate
---

# Is my SSL certificate valid? How to check it with the SSL Certificate Check

2026-10-04 · WatchFor Team · Guides · guides, ssl, security

The browser says "Your connection is not private" on one hostname and works on the next. To check an SSL certificate, connect to the server the way a browser does and read what it actually serves: who issued it, the dates, and every name it covers. Here's how to do it in seconds with the free SSL Certificate Check, how to read a wildcard SAN list, and the openssl commands that confirm it.

To check an SSL certificate, open a TLS connection to the host and read the
certificate it serves: the issuer, the *not before* and *not after* dates,
and the list of names (SANs) it covers. If the hostname you typed isn't on
that list, or a wildcard on it doesn't reach that far, browsers refuse the
connection no matter how new the certificate is.

The free [SSL Certificate Check](/ssl-checker) does that handshake from a
WatchFor probe, validates the chain and the name, and shows every field on
one screen. No signup.

## What this tool answers

*Is the certificate this server hands out valid for this name, right now,
and when does it run out?* Reach for it when a browser shows
`NET::ERR_CERT_COMMON_NAME_INVALID` or "Your connection is not private",
after installing or renewing a certificate, after pointing a new subdomain
at a load balancer or CDN, or before you tell a customer "it's fine on our
side". It checks the server, not your laptop, so a local proxy, antivirus
or stale browser cache can't fool it.

## A real example

### Checking an SSL certificate when one subdomain shows a privacy error

A typical report: `www` loads fine, but `shop.eu.example.com` (or any deeper
name) throws a privacy error, and nobody changed the certificate. The usual
cause is a wildcard that covers one label and no more. We showed how that
reads with a site that runs a long list of wildcards.

In the [SSL Certificate Check](/ssl-checker) we typed `wikipedia.org`, kept
port **443**, left the SNI field empty and pressed **Inspect certificate**.
Logged out, the check runs from the WatchFor location nearest you, Vilnius
in our case:

![The SSL Certificate Check result for wikipedia.org on port 443: 30 days until expiry in amber, a green VALID badge, subject *.wikipedia.org, issuer YE2 from Let's Encrypt, not before 2026-08-05T19:15:41Z, not after 2026-11-03T19:15:40Z, TLS 1.3, and the first entries of a list of 41 SANs](/blog/how-to-check-an-ssl-certificate-result.webp)

The handshake took 212 ms over TLS 1.3. The certificate is **valid**: the
chain builds to a trusted root, the name matches and today falls inside its
dates. It was issued by Let's Encrypt (intermediate `YE2`) on 5 August and
expires on 3 November 2026, **30 days** from the run. The SAN list holds
**41 names**, and that list is the part that answers the subdomain question.

A wildcard matches exactly one label at the left (RFC 6125). So for this
certificate:

- `wikipedia.org`: **covered** by the SAN `wikipedia.org` (a wildcard never
  covers the bare domain).
- `en.wikipedia.org`: **covered** by `*.wikipedia.org`.
- `en.m.wikipedia.org`: **covered** by `*.m.wikipedia.org`, a separate entry.
- `test.en.wikipedia.org`: **not covered**; `*.wikipedia.org` stops one
  label deep.

Wikimedia lists `*.m.wikipedia.org` separately precisely because
`*.wikipedia.org` can't serve the mobile hostnames.

To see what the failing case looks like we ran the check once more against
`wrong.host.badssl.com`, a public test host whose certificate is issued for
`*.badssl.com`:

![The SSL Certificate Check result for wrong.host.badssl.com on port 443: a red INVALID badge and the error type ssl_handshake_failed, with 0 days shown because no certificate was accepted](/blog/how-to-check-an-ssl-certificate-mismatch.webp)

**NAME MISMATCH** means the check refused the certificate during the
handshake, so there is no day count and no field list. The red box under the
header gives the reason: the certificate covers `*.badssl.com` and
`badssl.com`, not `wrong.host.badssl.com`. The certificate is fine in every
other way; it just isn't for this name. `*.badssl.com` covers
`host.badssl.com`, but `wrong.host.badssl.com` has two labels in front of
`badssl.com`. The command-line section below prints the exact reason.

**The answer:** for the reported subdomain, read the SAN list. If the exact
name or a wildcard one level above it isn't there, the fix is a certificate
that lists it: add the name (or `*.eu.example.com`) to the next issuance,
for example `certbot certonly -d example.com -d '*.example.com' -d
'*.eu.example.com'` with a DNS challenge, since Let's Encrypt only issues
wildcards over DNS-01.

## How to read the results

| What you see | What it means |
| --- | --- |
| **Days until expiry** | Whole days until *not after*. Green from 45 days, amber from 14 to 44, red under 14. A 90-day Let's Encrypt certificate sits around 30 days at renewal time, so amber there is normal. |
| **VALID or the failure** | VALID means the handshake verified the chain against trusted roots, the name matched and today is inside the validity dates. Otherwise the badge names what failed: NAME MISMATCH, EXPIRED, UNTRUSTED, TIMEOUT, NO CONNECTION or HANDSHAKE FAILED. |
| **Reason** | Shown on failures, in a red box: the cause in one sentence, e.g. which names the certificate does cover. |
| **host:port** | What was checked. Port matters: mail, databases and admin panels often serve a different certificate than 443. |
| **Subject** | The certificate's common name. Browsers ignore it for matching; only the SANs count. |
| **Issuer / Issuer org** | The intermediate that signed it and its CA (`YE2`, Let's Encrypt). Handy when you expected a different CA after a migration. |
| **Not before / Not after** | The validity window in UTC. A *not before* in the future breaks clients whose clocks are behind. |
| **Version** | The TLS version this handshake negotiated, not every version the server accepts; that's the [SSL/TLS grade checker](/ssl-grade-checker). |
| **Serial** | The certificate's serial number. Compare it with the file on your server to prove which certificate is live. |
| **SANs (n)** | Every hostname and IP the certificate covers. The list scrolls. |

**Share** (signed in) saves a link to the exact result.

## More ways to use it

- **After a renewal.** Check that *not before* is today's date and the
  serial changed. If not, the server is still serving the old file; reload
  it. The [SSL expiry guide](/blog/how-to-check-ssl-certificate-expiry)
  walks through that case.
- **Behind a CDN or load balancer.** Check the public hostname, then the
  origin by IP with the hostname in **SNI server name**. A valid edge
  certificate in front of an expired origin certificate breaks strict
  origin pulls.
- **Mail and other ports.** Put `465` or `993` in **Port** for implicit-TLS
  mail, or `8443` for an admin panel. Port 25 with STARTTLS needs the
  [SMTP test](/smtp-test) instead.
- **Before a vendor go-live.** Point a CNAME at a SaaS vendor and check the
  name once their certificate is issued, before you announce the URL.

## Tips and gotchas

- **A certificate the check can't verify fails the handshake.** Expired,
  self-signed, untrusted root or wrong name: you get the failure and its
  reason instead of the field list, as above. A missing intermediate and a
  self-signed root both read as UNTRUSTED; add `-showcerts` to the
  `openssl s_client` command below to see the chain the server sent.
- **"Valid here, broken for one user"** usually means a missing
  intermediate that their client can't fetch on its own, a clock that's
  wrong, or a corporate proxy re-signing traffic. Serve the full chain
  (`fullchain.pem`, not `cert.pem`).
- **SNI decides which certificate you get.** One IP can host hundreds of
  certificates. When you test by IP, put the hostname in the SNI field, or
  you'll see the server's default certificate.
- **One location, one front end.** The check runs from one probe. Behind
  GeoDNS or anycast, another region may hit a server with a different
  certificate.
- **It doesn't check revocation** (OCSP/CRL). The
  [SSL/TLS grade checker](/ssl-grade-checker) reports OCSP stapling.

## Check it from the command line too

`openssl s_client` does the same handshake and `openssl x509` prints the
fields:

```bash
echo | openssl s_client -connect wikipedia.org:443 -servername wikipedia.org 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates
```

```text
subject=CN=*.wikipedia.org
issuer=C=US, O=Let's Encrypt, CN=YE2
notBefore=Aug  5 19:15:41 2026 GMT
notAfter=Nov  3 19:15:40 2026 GMT
```

Save the certificate and ask it directly whether a name is covered:

```bash
echo | openssl s_client -connect wikipedia.org:443 -servername wikipedia.org 2>/dev/null \
  | openssl x509 > wp.pem
openssl x509 -in wp.pem -noout -checkhost en.m.wikipedia.org
openssl x509 -in wp.pem -noout -checkhost test.en.wikipedia.org
```

```text
Hostname en.m.wikipedia.org does match certificate
Hostname test.en.wikipedia.org does NOT match certificate
```

`curl` gives the browser-style verdict for the broken host:

```bash
curl -sS -o /dev/null https://wrong.host.badssl.com/
```

```text
curl: (60) SSL: no alternative certificate subject name matches target hostname 'wrong.host.badssl.com'
```

## Keep watching it

A check today says nothing about the renewal that silently fails next
month. An [SSL monitor](/ssl-monitoring) re-checks the certificate on a
schedule from several locations, warns when fewer than 14 days remain (you
can raise it), and has **Hostname Match** and **Certificate Chain** rules
that open an incident when a renewal ships without a name you need or with
an incomplete chain. [How to never get caught by an expired SSL
certificate](/blog/ssl-certificate-expiry) covers the setup.

## FAQ

**How do I check if an SSL certificate is valid?**
Connect to the host on port 443 and verify three things: the chain ends at a
trusted root, the hostname is in the SAN list, and today is between the
*not before* and *not after* dates. The SSL Certificate Check does all three
in one run; `openssl s_client -connect host:443 -servername host` does it
from a terminal.

**Does a wildcard certificate cover subdomains of subdomains?**
No. `*.example.com` covers `shop.example.com` but not `eu.shop.example.com`
and not `example.com` itself. Add `*.shop.example.com` or the exact name to
the certificate.

**Why does my browser show "Your connection is not private" when the
certificate is valid?**
Usually the certificate is valid but not for that name, the server isn't
sending the intermediate certificate, or your device clock is wrong. Check
the SAN list and the chain from outside your network first. How the
handshake works is in [How HTTPS works](/blog/how-https-works).

---

Canonical page: https://watchfor.io/blog/how-to-check-an-ssl-certificate · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
