CERTCHECK(1)toolbelt manualCERTCHECK(1)

certcheck

Days until a TLS certificate expires.

Synopsis

certcheck [options] HOST[:PORT] | FILE ... [-- s_client options]
certcheck [options] -f LIST

Description

Every HTTPS site, mail server and API has a TLS certificate with an end date. After that date browsers show a full-page warning, curl refuses to connect, and mail servers stop delivering. Let's Encrypt certificates last 90 days and renew themselves, until the renewal job breaks quietly. certcheck tells you how many days a certificate has left, who issued it and which names it covers, so you find a broken renewal weeks before your users do.

Give it a host, and it connects with openssl s_client, reads the certificate the server sends, and hands it to openssl x509. Give it a file, and it reads the file with openssl x509 alone, with no network. A .pem, .crt or .cer file in PEM form works, and so does DER, the binary form. For a file with a whole chain, such as fullchain.pem, it reads the first certificate, which is the one for your site.

One target prints one line. Several targets, or -f LIST, print a table and a summary line. Either way it exits 1 when any certificate has fewer days left than the warning window, has expired, or cannot be read, so a cron job or a CI step can act on it.

certcheck checks dates. It does not check that the chain leads to a trusted root, that the name matches, or that the server's TLS settings are sound. Pass -- -verify_return_error to make s_client fail on an untrusted chain.

Options

OptionWhat it does
-w, --warn DAYSThe warning window. Exit 1 when fewer days are left. 30 by default. -w 0 flags only expired ones.
-f, --file LISTRead targets from a file, one per line. - reads standard input. Blank lines and text after # are ignored. Targets on the command line are checked too.
-t, --timeout SECSGive up on a host after this many seconds. 5 by default.
-q, --quietIn a table, show only the rows with a problem, then the summary line.
-v, --verboseShow each step and each openssl command before it runs.
-h, --helpShow the help.

Targets

A target is a host, a host and port, a URL or a file.

You typecertcheck connects to
example.comexample.com:443
example.com:8443example.com:8443
https://example.com/loginexample.com:443. The scheme and path are dropped.
192.0.2.10192.0.2.10:443, with no server name sent
[2001:db8::1]:8443that IPv6 address on port 8443
./site.pem, site.crt, /etc/ssl/x.pemno connection, it reads the file

A target counts as a file when the file exists, when it has a / in it, or when it ends in .pem, .crt, .cer or .der. A missing file gives no such file rather than a DNS lookup of site.pem.

For a name, certcheck sends it as the server name (SNI). One server often hosts many sites and picks the certificate by that name. An IP address gets no server name, so you see the server's default certificate.

How it decides

Pass-through

Options after -- go to openssl s_client, for hosts only. The common use is a mail or database port that starts in plain text and switches to TLS.

certcheck smtp.example.com:587 -- -starttls smtp
certcheck imap.example.com:143 -- -starttls imap
certcheck db.example.com:5432 -- -starttls postgres

Port 465 for SMTP and 993 for IMAP speak TLS from the start and need no pass-through.

Needs

openssl, from the openssl package on every distro. timeout from coreutils is used for --timeout when it is there. Without it, a host that never answers can make certcheck wait for the system's TCP timeout, which is often two minutes.

Examples

One site

certcheck example.com
example.com  87 days  SSL Corporation Cloudflare TLS Issuing ECC CA 3  example.com *.example.com

Every site in one run

cat hosts.txt
# production sites
./shop.pem
./api.pem
./many.pem
intranet.example.com
certcheck -f hosts.txt
HOST                  DAYS  ISSUER               EXPIRES
./shop.pem              61  Let's Encrypt E7     2026-11-29
./api.pem               12  Let's Encrypt R12    2026-10-11  warn
./many.pem             200  Amazon RSA 2048 M02  2027-04-17
intranet.example.com     -  -                    -           error, not found in DNS
4 certificates, 1 warn, 1 failed
echo $?
1

Only the problems, for cron

certcheck -q -f hosts.txt
./api.pem               12  Let's Encrypt R12    2026-10-11  warn
intranet.example.com     -  -                    -           error, not found in DNS
4 certificates, 1 warn, 1 failed

A crontab line that mails you only when something is wrong:

0 8 * * * certcheck -q -w 21 -f ~/hosts.txt >/tmp/certs.txt || mail -s "certificates" you@example.com </tmp/certs.txt

A file on disk, with the commands shown

certcheck -v ./api.pem
certcheck: reading ./api.pem
+ openssl x509 -in ./api.pem -noout -enddate -issuer -subject -ext subjectAltName -nameopt multiline
./api.pem  12 days  Let's Encrypt R12  api.example.com
certcheck: ./api.pem expires on 2026-10-11, inside the 30 day warning window

A certificate with many names

certcheck many.pem
many.pem  200 days  Amazon RSA 2048 M02  a.example.com b.example.com c.example.com d.example.com +2 more

Did the renewal work?

Run it against the file certbot wrote and against the live site. When the file has the new date and the site has the old one, the web server has not reloaded the new certificate yet.

sudo certcheck /etc/letsencrypt/live/shop.example.com/cert.pem shop.example.com

A host that does not answer

certcheck intranet.example.com
certcheck: cannot find intranet.example.com in DNS
certcheck -t 2 10.0.0.99
certcheck: cannot connect to 10.0.0.99:443, timed out after 2s

Troubleshooting

certcheck: cannot connect to HOST:443, connection refused
Nothing listens on that port. Check the port, and check it from the server with port 443.
certcheck: cannot connect to HOST:443, timed out after 5s
A firewall drops the packets, or the host is down. Raise the limit with -t 15 for a slow network.
certcheck: HOST:25 does not speak TLS here. For a mail port try: certcheck HOST:25 -- -starttls smtp
The port speaks plain text first. Pass the right -starttls protocol after --.
certcheck: FILE is not a certificate, openssl x509 cannot read it
The file holds something else, often the private key. Certbot keeps the certificate in cert.pem and the key in privkey.pem.
The days differ from what the browser shows
The site may serve a different certificate for another name. Give the exact name the browser uses, since certcheck sends it as the server name.

Exit status

CodeMeaning
0Every certificate has at least the warning window left.
1A certificate expires inside the warning window, has expired, or could not be read.
2Bad usage, such as no target or a --warn that is not a number.
3openssl is not installed.

See also

dnscheck, httptime, openssl-s_client(1), openssl-x509(1)