Skip to content
shellmap

Check SSL certificate expiry

Print the notBefore / notAfter dates of a remote site's TLS certificate (or a local .pem file).

How to check ssl certificate expiry in each shell

Bashunix
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
Zshunix
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
Fishunix
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
PowerShellwindows
$r = [Net.WebRequest]::Create("https://example.com"); $r.AllowAutoRedirect = $false; $r.GetResponse().Close(); $r.ServicePoint.Certificate | Select-Object Subject, Issuer, @{N="NotAfter";E={$_.GetExpirationDateString()}}

pwsh 7 alternative without raw .NET: `(Invoke-WebRequest -Uri https://example.com).BaseResponse.RequestMessage.Headers` — but `[Net.WebRequest]` still gives the easiest path to the X509Certificate object on both 5.1 and 7+. For local `.pem` files: `(New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 "cert.pem").NotAfter`.

cmd.exewindows
powershell -NoProfile -Command "$r = [Net.WebRequest]::Create('https://example.com'); $r.AllowAutoRedirect = $false; $r.GetResponse().Close(); $r.ServicePoint.Certificate.GetExpirationDateString()"

cmd ships `curl.exe` but `curl` doesn't expose cert dates cleanly. PowerShell shell-out is the practical answer.

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • `-servername` is the SNI hostname — REQUIRED on any modern HTTPS server (every shared-host CDN edge: Cloudflare, Fastly, AWS CloudFront). Without `-servername`, you get the edge's default certificate (often `*.cloudflareedge.com` instead of your actual site), which expires on its own schedule and is almost never what you want to monitor.
  • The `echo |` redirect terminates `openssl s_client`'s interactive prompt — without it, the command hangs waiting for input. macOS BSD `openssl` is LibreSSL by default (`/usr/bin/openssl --version` reports `LibreSSL`), not OpenSSL — most flags are compatible but some (`-trace`, `-msg`) differ. `brew install openssl@3` for the GNU one; binary at `/opt/homebrew/opt/openssl@3/bin/openssl`.
  • Days-until-expiry one-liner: `echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate | sed 's/notAfter=//' | xargs -I{} date -d "{}" +%s | awk -v now=$(date +%s) '{print int(($1-now)/86400)}'`. macOS `date` doesn't accept `-d`; use `date -j -f "%b %e %T %Y %Z" "$STR" +%s` or do the math in PowerShell.
  • For automated monitoring (alerting on expiry < 30 days): `nagios-plugins-network` ships `check_http -C 30` and `check_ssl_cert`. Don't roll your own cron — Let's Encrypt renewal cycles, intermediate-CA rotations, and chained-cert expiries are subtle. Better: pay for/run uptimerobot, Pingdom, or self-hosted Uptime Kuma which handle SNI + chain validation correctly.

Related commands

Related tasks