# Check when a certificate expires

To read a certificate's expiry date, run `openssl x509 -in certificate.crt -noout -enddate`. For a live server, pipe `s_client` into `x509`: `openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -enddate`. In monitoring, use `-checkend` instead of parsing dates — `openssl x509 -in certificate.crt -noout -checkend 2592000` exits 0 if the certificate is still valid 30 days from now and 1 if it is not, which is exactly what a check script needs.

Source: https://certguru.io/openssl/check-certificate-expiry/
Last updated: 2026-08-19
Commands verified against OpenSSL 3.5.7.

---

## From a file

*Expiry date only*

```bash
openssl x509 -in certificate.crt -noout -enddate
```

Expected output:

```
notAfter=Nov 14 23:59:59 2026 GMT
```

*Both ends of the validity window*

```bash
openssl x509 -in certificate.crt -noout -dates
```

Expected output:

```
notBefore=Aug 16 00:00:00 2026 GMT
notAfter=Nov 14 23:59:59 2026 GMT
```

> **Check notBefore too**
>
> A certificate that is not yet valid fails exactly as hard as an expired one, and the error is less
>   familiar so it takes longer to recognise. It happens more often than you would expect when a
>   server's clock is wrong.

## From a live server

*What the server is actually serving right now*

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

Expected output:

```
notAfter=Nov 14 23:59:59 2026 GMT
```

> **-servername is not optional**
>
> Without it OpenSSL sends no SNI, and a virtual-hosted server answers with its default certificate.
>   You then monitor the expiry of a certificate nobody is using.

## The form you want in monitoring

Parsing and comparing dates in shell is fiddly and locale-sensitive. `-checkend` does the comparison
for you and answers with an exit code:

*Is it still valid 30 days from now?*

```bash
openssl x509 -in certificate.crt -noout -checkend 2592000
```

Expected output:

```
Certificate will not expire
```

*Branch on the exit code*

```bash
if openssl x509 -in certificate.crt -noout -checkend 2592000; then
  echo "OK — more than 30 days left"
else
  echo "WARNING — expires within 30 days"
fi
```

| Seconds | Window |
| --- | --- |
| `0` | Expired right now |
| `604800` | 7 days |
| `2592000` | 30 days |
| `5184000` | 60 days |
| `7776000` | 90 days |

## Checking a live server in a script

*Exit non-zero if a host's certificate expires within 30 days*

```bash
openssl s_client -connect example.com:443 -servername example.com /dev/null \
  | openssl x509 -noout -checkend 2592000
```

Wrapped into something you can run over a list of hosts:

*Check several hosts at once*

```bash
for host in example.com api.example.com www.example.com; do
  if openssl s_client -connect "$host:443" -servername "$host" /dev/null \
     | openssl x509 -noout -checkend 2592000 >/dev/null; then
    echo "OK       $host"
  else
    echo "EXPIRING $host"
  fi
done
```

## Days remaining as a number

Useful when feeding a metrics system rather than an alert:

*Print days until expiry*

```bash
end=$(openssl x509 -in certificate.crt -noout -enddate | cut -d= -f2)
echo $(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
```

> **macOS date is different**
>
> BSD `date` does not accept `-d`. On macOS use
>   `date -j -f "%b %e %T %Y %Z" "$end" +%s`, or install GNU coreutils and use `gdate`.

## Do not forget the chain

A leaf certificate with plenty of time left is still broken if an intermediate above it has expired,
and checking only the leaf will not show it:

*Check every certificate in a bundle*

```bash
openssl crl2pkcs7 -nocrl -certfile fullchain.crt \
  | openssl pkcs7 -print_certs \
  | openssl x509 -noout -subject -enddate 2>/dev/null
```

See [verify a certificate chain](/openssl/verify-certificate-chain/).


## Frequently asked questions

### What does -checkend return?

An exit code, not a date. It exits 0 if the certificate is still valid the given number of seconds from now, and 1 if it will have expired. It also prints a line of text, but the exit code is what a script should branch on.

### How do I check expiry without OpenSSL?

On a live server you can use `curl -vI https://example.com` and read the certificate lines, or `echo | openssl s_client` if OpenSSL is present but you dislike the syntax. There is no reliable way to read a certificate file's dates without a tool that parses ASN.1.

### Why does my certificate look valid but browsers say it is not?

Check the notBefore date as well as notAfter, and check the whole chain — an expired intermediate invalidates everything below it while the leaf still looks fine. See verifying a chain.

### What time zone are the dates in?

Always GMT/UTC. X.509 requires it, and OpenSSL prints the GMT suffix to make that explicit.


## References

- [openssl-x509(1)](https://docs.openssl.org/3.5/man1/openssl-x509/) — -checkend and -dates
- [RFC 5280: X.509 and CRL profile](https://www.rfc-editor.org/rfc/rfc5280) — the specification every field on a certificate comes from

## Related pages

- https://certguru.io/openssl/view-certificate/
- https://certguru.io/openssl/get-certificate-from-server/
- https://certguru.io/errors/certificate-has-expired/
- https://certguru.io/openssl/verify-certificate-chain/
- https://certguru.io/concepts/certificate-chain/
- https://certguru.io/concepts/ocsp-vs-crl/

--
CertGuru — https://certguru.io
