How to check when an SSL certificate expires
The short answer
Every TLS certificate has a notAfter date. Once that date passes, browsers show a full-page warning. Chrome's version says NET::ERR_CERT_DATE_INVALID. You can read the date in a browser for one site, or with a command to check many sites or to script a check.
1. In the browser (one site, no tools)
- Chrome / Edge: click the site-information icon to the left of the address bar, open the connection details, then open the certificate viewer. The Validity section shows "Expires on".
- Firefox: click the padlock, then Connection secure, then More information, then View Certificate. Look for Not After.
- Safari: click the padlock, then Show Certificate, then expand Details.
The browser shows the certificate your connection received. If the site sits behind a CDN, that is the CDN's edge certificate, which may not be the one installed on the origin server. See the pitfalls section below.
2. With openssl (macOS, Linux, WSL)
`bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -enddate
`
Output:
`
notAfter=Mar 3 12:00:00 2027 GMT
`
Notes:
-servernamesends SNI. Leave it out and a shared host may hand back its default certificate, which is the wrong one.echo |closes the connection straight away so the command doesn't hang.- Add
-subject -issuerafter-enddateto confirm you're looking at the right certificate.
Is it expiring soon? Get a yes/no answer
-checkend takes a number of seconds. It exits with status 1 if the certificate expires within that window:
`bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -checkend $((21*86400)) \
&& echo "OK for 21+ days" || echo "Expires within 21 days"
`
That exit code is all a cron job or CI step needs.
3. With curl
`bash
curl -svI https://example.com 2>&1 | grep -i "expire date"
`
This works when curl is built with OpenSSL, which is the usual case on Linux. Some builds, such as Schannel on Windows or Secure Transport on older macOS, don't print the date. If you see nothing, use openssl instead.
4. With PowerShell (Windows, no extra installs)
`powershell
$h = "example.com"
$tcp = New-Object Net.Sockets.TcpClient($h, 443)
$ssl = New-Object Net.Security.SslStream($tcp.GetStream(), $false, { $true })
$ssl.AuthenticateAsClient($h)
$cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2($ssl.RemoteCertificate)
"{0} expires {1} ({2} days)" -f $h, $cert.NotAfter, ($cert.NotAfter - (Get-Date)).Days
$ssl.Dispose(); $tcp.Dispose()
`
The { $true } callback accepts any certificate, so the script can still read the date from one that has already expired.
5. On the server itself
If you have shell access and know where the file is:
`bash
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -enddate
`
If you use Certbot, sudo certbot certificates lists every certificate it manages with its expiry date and days remaining.
Pitfalls that give you the wrong date
- CDN or proxy in front. Cloudflare and similar services present their own edge certificate. Your origin certificate can expire without visitors noticing, until the proxy refuses to connect to the origin (for example in Cloudflare's Full (strict) mode). To check the origin directly, connect to its IP while still sending the real hostname:
openssl s_client -connect 203.0.113.10:443 -servername example.com. wwwand the bare domain. They can have separate certificates. Check both.- Several servers behind one name. A load balancer or several A/AAAA records can mean one server renewed and another didn't. Check each IP.
- Other services. Mail (
-connect mail.example.com:465, or:587 -starttls smtp), APIs and admin panels have their own certificates. - Wrong local clock. If one visitor gets a date warning and nobody else does, check the clock on their device before touching the server.
Why checking matters more now
The CA/Browser Forum is shortening the maximum lifetime of public certificates (ballot SC-081). The cap is 200 days for certificates issued from March 15, 2026, 100 days from March 15, 2027, and 47 days from March 15, 2029 (DNSimple summary). Shorter lifetimes mean more renewals, and each renewal is another chance for one to fail without anyone noticing. Let's Encrypt also stopped sending expiration reminder emails in June 2025, so you can no longer count on it to warn you.
If you're checking more than a handful of sites
The commands above work well for one site. For dozens, wrap the openssl command in a loop over a list of domains, sort the results by days left, and schedule it. We're building Lapsewatch for agencies that need exactly this. It will keep one list of every client domain with days-to-expiry and send an email or Slack alert before any certificate lapses. It isn't live yet; you can follow its progress on the page below.