Guide

How to check SSL certificate expiry

Four methods, in the order most people need them. Every command below was run against a live host, and the output is what it printed — including the one that fails on macOS.

Method 1 — openssl s_client

The most direct answer. Pipe a handshake into x509 and ask for the dates. The -servername flag sends SNI, which is required on any host that serves more than one certificate — omit it and you may be shown the wrong one.

Validity window
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -dates
notBefore=Jul 29 22:10:08 2026 GMT
notAfter=Oct 27 22:17:21 2026 GMT

Both timestamps are GMT/UTC. notAfter is the expiry date.

Who issued it, and for what name
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -subject -issuer
subject= /CN=example.com
issuer= /C=US/O=SSL Corporation/CN=Cloudflare TLS Issuing ECC CA 3
Did the chain validate
echo | openssl s_client -servername example.com -connect example.com:443 2>&1 \
  | grep 'Verify return code'
    Verify return code: 0 (ok)

Any non-zero code is a trust failure. This is a separate question from expiry — a current certificate can still fail here.

How many certificates the server sent
echo | openssl s_client -servername example.com -connect example.com:443 -showcerts 2>/dev/null \
  | grep -c 'BEGIN CERTIFICATE'
4

A count of 1 on a publicly issued certificate usually means the intermediate is missing from the server configuration.

Method 2 — curl

Already installed nearly everywhere, and the verbose output includes the certificate summary without a second command.

Dates, subject, and issuer in one call
curl -sS -o /dev/null -vI https://example.com 2>&1 \
  | grep -E 'start date|expire date|subject:|issuer:'
*  subject: CN=example.com
*  start date: Jul 29 22:10:08 2026 GMT
*  expire date: Oct 27 22:17:21 2026 GMT
*  issuer: C=US; O=SSL Corporation; CN=Cloudflare TLS Issuing ECC CA 3

curl sends SNI automatically from the URL, so there is no equivalent of the -servername trap here.

Method 3 — script it with an exit code

For a cron job or a CI step, you want a pass/fail rather than text to read. openssl x509 -checkend does exactly that.

Fail if the certificate expires within 30 days
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -checkend 2592000
echo "exit=$?"
exit=0

2592000 is 30 days in seconds. Exit 0 means it will still be valid then; exit 1 means it expires within the window. Note the inversion — the failing case is the one that exits non-zero, which is what you want in CI.

Portability traps worth knowing

These are the differences that turn a working command into a confusing error on someone else's machine.

  • macOS ships LibreSSL, not OpenSSL. openssl x509 -ext subjectAltName fails there with “unknown option -ext”; use openssl x509 -noout -text | grep -A1 'Subject Alternative Name' instead, which works on both.
  • Without -servername, s_client omits SNI and a shared-IP host may present a different certificate from the one your visitors see.
  • Without the leading echo, s_client holds the connection open and waits for input.
  • 2>/dev/null hides the handshake diagnostics — drop it when you are debugging a trust failure rather than reading dates.
  • All certificate timestamps are UTC. Converting them to local time before comparing is a common source of off-by-one deadlines.

Method 4 — check it in the browser

Useful for a quick look, unreliable as a verification method, for the reasons listed underneath.

  1. Open the certificate viewer

    Select the padlock in the address bar, then the connection or certificate entry. Every major browser exposes the validity window and the issuer this way.

  2. Read the validity period

    The “not after” or “expires on” date is the one that matters. Browsers usually render it in your local timezone rather than UTC.

  3. Treat trust with suspicion

    A browser can complete a chain using an intermediate it cached from another site. That makes it the worst tool for spotting a server that is not sending its full chain.

Method 5 — stop checking manually

Every method above is a snapshot that is true only at the moment you run it. The failure mode of manual checking is forgetting.

  1. Run the check here first

    The ExpiryBeacon checker performs the same server-side handshake and returns validity, trust, issuer, covered names, chain depth, protocol, cipher, and fingerprint in one result.

  2. Save the endpoint as a monitor

    Signing in with Google puts the endpoint on a daily scheduled check at 08:00 UTC and keeps a durable history of what each check found.

  3. Set a warning window and turn alerts on

    Choose 30, 14, or 7 days per monitor. With email alerts enabled, a state change sends one message — entering the window, losing trust, expiring, failing a check, or recovering — with repeats suppressed.

Questions about check ssl certificate expiry

What is the quickest command to check SSL certificate expiry?

echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates prints the notBefore and notAfter timestamps in UTC.

How do I check certificate expiry in a script?

Use openssl x509 -noout -checkend followed by a number of seconds. It exits 1 if the certificate expires within that window and 0 if it does not, so a CI step fails only when action is needed.

Why does -ext subjectAltName fail on my Mac?

macOS ships LibreSSL rather than OpenSSL, and LibreSSL's x509 command has no -ext option. openssl x509 -noout -text | grep -A1 'Subject Alternative Name' returns the same information on both.

Do these commands work for a certificate on a non-standard port?

Yes — change the port after the colon in -connect. ExpiryBeacon's own checker supports 443, 8443, and 9443.