# How to check SSL certificate expiry from the command line

> One openssl command prints the expiry date of any HTTPS certificate. This page shows it, plus tested variants for days left, -checkend, certificate files and mail servers.

Source: https://sslmonitor.com/check-ssl-certificate-expiry-command-line/

## The short answer

Run this, replacing `example.com` with your domain:

```bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate
```

It prints one line with the expiry date in UTC:

```text
notAfter=Jan 14 23:59:59 2027 GMT
```

`openssl s_client` opens a TLS connection and prints the server's certificate. `echo |` closes the connection right after the handshake. `-servername` sends the hostname with SNI, so a server that hosts many sites returns the certificate for the name you asked for. OpenSSL 1.1.1 and later send the `-connect` hostname by default, but older versions do not, so setting it keeps the command portable. `openssl x509 -noout -enddate` reads the certificate and prints only its `notAfter` field. This is the same command SSLMonitor runs for each domain. The [s_client](https://docs.openssl.org/3.0/man1/openssl-s_client/) and [x509](https://docs.openssl.org/3.0/man1/openssl-x509/) manual pages document every option used here.

The examples on this page were tested with OpenSSL 3.0 and GNU coreutils on Linux.

## Show start date, end date, subject and issuer

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

```text
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R12
notBefore=Oct 16 23:59:59 2026 GMT
notAfter=Jan 14 23:59:59 2027 GMT
```

The issuer line above is sample output. Yours shows the certificate authority that signed your certificate.

## Print the date in ISO 8601 format

OpenSSL 3.0 and later can print dates in a format that sorts and parses cleanly:

```bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate -dateopt iso_8601
```

```text
notAfter=2027-01-14 23:59:59Z
```

## Check whether a certificate expires within N days

`-checkend` takes a number of seconds. It prints a message and sets the exit code: `0` if the certificate is still valid at that time, `1` if it will have expired. 30 days is 2,592,000 seconds:

```bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -checkend 2592000
echo "exit code: $?"
```

```text
Certificate will not expire
exit code: 0
```

The exit code makes it easy to use in a script or a monitoring check:

```bash
if ! echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -checkend 2592000 > /dev/null; then
  echo "example.com expires within 30 days (or could not be checked)"
fi
```

A failed connection also returns a non-zero exit code, so the message covers both cases.

## Count the days left

This is the calculation SSLMonitor uses. It needs GNU `date`:

```bash
end=$(echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate | cut -d= -f2)
echo $(( ($(date -d "$end" +%s) - $(date +%s)) / 86400 ))
```

The result is whole days, rounded down. A certificate that expires in 9 days and 20 hours prints `9`.

## List the names a certificate covers

A certificate is only valid for the names in its Subject Alternative Name extension. To list them (OpenSSL 1.1.1 or later). The names come from [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6), the standard for X.509 certificates:

```bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName
```

```text
X509v3 Subject Alternative Name:
    DNS:example.com, DNS:www.example.com
```

## Check a certificate file on disk

To read a certificate file directly, without a network connection:

```bash
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -enddate
```

Comparing this date with the one your server returns tells you whether a renewed certificate has been installed and the web server reloaded. If the file is newer than what the server sends, reload the web server.

## Check mail servers with STARTTLS

Mail servers often start in plain text and upgrade with STARTTLS. Add `-starttls` with the protocol name:

```bash
# SMTP submission (port 587)
echo | openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com 2>/dev/null \
  | openssl x509 -noout -enddate

# IMAP (port 143)
echo | openssl s_client -connect mail.example.com:143 -starttls imap -servername mail.example.com 2>/dev/null \
  | openssl x509 -noout -enddate
```

Ports that use TLS from the first byte, such as 465 (SMTPS), 993 (IMAPS) and 995 (POP3S), work like HTTPS: use `-connect host:993` without `-starttls`. SSLMonitor itself only checks port 443.

## Read the dates with curl

[curl](https://curl.se/docs/manpage.html) with `-v` prints the certificate dates in its connection details (wording can vary between curl versions and TLS libraries):

```bash
curl -sv -o /dev/null https://example.com 2>&1 | grep -E 'start date|expire date'
```

```text
*  start date: Oct 16 23:59:59 2026 GMT
*  expire date: Jan 14 23:59:59 2027 GMT
```

## Why the command can hang or return nothing

- **Nothing printed:** `openssl s_client` could not connect or the server did not complete a TLS handshake. Run it without `2>/dev/null` to see the error, such as `Connection refused` or a DNS failure.
- **Wrong certificate:** the server received no SNI name (for example, you connected to an IP address without `-servername`, or used OpenSSL older than 1.1.1), so it returned its default certificate.
- **Long wait:** a firewall drops packets instead of rejecting them, so `openssl` waits for the TCP connect timeout. Wrap the command in `timeout 10` to cap it.
- **Behind a proxy:** a TLS-inspecting proxy returns its own certificate, so check from a network without one.

## Run these checks every day

SSLMonitor wraps the first command on this page in a loop over your domain list, works out the days left, and emails a report when a certificate is close to expiry. The [install guide](/install/) takes a few minutes.
