Reference

Check SSL certificates with curl: ssl.sh command line

By · Last verified

curl ssl.sh/example.com connects to example.com on port 443, reads the certificate chain the server sends and prints a summary: validity, days left, issuer, chain completeness, trust, TLS version and a grade. Nothing needs to be installed.

Quick start

Every URL below works with plain curl. In scripts, use the full https:// URL and -f so that failures produce a non-zero exit code.

curl ssl.sh/example.com              # summary with colors
curl ssl.sh/example.com:8443         # another port
curl ssl.sh/example.com/days         # days until expiry, integer only
curl ssl.sh/example.com/chain        # full chain PEM
curl ssl.sh/example.com/json         # JSON
curl ssl.sh/ct/example.com           # Certificate Transparency search
curl ssl.sh/help                     # usage

Example output (colors removed):

example.com:443  ● VALID  Grade A  64 days left
────────────────────────────────────────────────────────────
Subject    example.com
SANs       example.com, www.example.com
Issuer     R12 · Let's Encrypt
Valid      2026-10-15 → 2027-01-13 (90d)
Key        RSA 2048 · sha256WithRSAEncryption · DV
TLS        TLSv1.3 · TLS_AES_256_GCM_SHA384 · h2
Chain      2 certificates · complete · trusted (ISRG Root X1)
IP         203.0.113.10

✓ Certificate is valid and trusted, expires in 64 days.
✓ The certificate chain is complete.
✓ TLS 1.3 is supported.

Full report: https://ssl.sh/example.com

Problems are listed with ! (warning) or ✗ (error) and a link to the page that explains the fix, for example NET::ERR_CERT_DATE_INVALID for an expired certificate.

Paths

Path Output
/<host> Summary. Port 443.
/<host>:<port> Same for another port, e.g. /mail.example.com:465.
/<host>/days Whole days until expiry (negative if expired). On failure: empty body, HTTP 4xx/5xx.
/<host>/chain Leaf certificate and intermediates as PEM, root excluded. Missing intermediates are downloaded via the AIA extension, so the output works as fullchain.pem.
/<host>/json The full result as JSON (same as /api/v1/check).
/ct/<domain> Certificates for the domain and its subdomains from CT logs, newest first.

You can paste a URL: curl ssl.sh/https://example.com/login checks example.com. Internationalized domain names are converted to punycode.

Query options

Option Effect
?T or ?nocolor Plain text without ANSI colors.
?full Also test TLS 1.0, 1.1, 1.2 and 1.3 separately, read the HSTS header, look up CAA records and check revocation (OCSP, then CRL). Slower.
?fresh=1 Skip the 5-minute cache.
?starttls=smtp Force STARTTLS: smtp, imap, pop3, ftp, xmpp or postgres.
?lang=zh Chinese labels and messages.
?json JSON on any path (or send Accept: application/json).

Combine options with &, and quote the URL in the shell: curl 'ssl.sh/example.com?full&T'.

Ports and STARTTLS

The checker accepts these ports: 443, 8443, 4443, 9443, 10443, 465, 587, 25, 993, 995, 143, 110, 21, 990, 636, 853, 5222, 5432, 6443, 2083, 2087 and 8883. STARTTLS is used automatically on 25 and 587 (SMTP), 143 (IMAP), 110 (POP3), 21 (FTP), 5222 (XMPP) and 5432 (PostgreSQL); the others are checked as implicit TLS.

curl ssl.sh/smtp.example.com:587
curl ssl.sh/imap.example.com:993

Use in scripts

Alert when a certificate has less than 14 days left:

#!/usr/bin/env bash
host=example.com
days=$(curl -fsS "https://ssl.sh/$host/days") || { echo "check failed for $host"; exit 2; }
if [ "$days" -lt 14 ]; then
  echo "$host: certificate expires in $days days"
  exit 1
fi

Save the complete chain for Nginx after fixing a missing intermediate:

curl -fsS https://ssl.sh/example.com/chain > fullchain.pem
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout

Extract fields with jq:

curl -fsS https://ssl.sh/example.com/json | jq '{grade, days_left, issuer: .chain[0].issuer_cn}'

Grades

Grade Meaning
A+ Trusted, only TLS 1.2/1.3 enabled and HSTS with max-age of at least 180 days (needs ?full).
A Trusted and valid, no weak protocol, cipher or key found.
B TLS 1.0/1.1 enabled or a weak cipher suite negotiated.
C Weak key (RSA under 2048 bits) or a SHA-1 signature in the chain.
F Expired, not yet valid, wrong host name, revoked, or only outdated TLS works.
T Not trusted: self-signed, unknown root, or a chain that cannot be completed.

Limits and privacy

  • 30 checks per minute per IP address. Cached results (5 minutes) do not count. With an API key (Authorization: Bearer <key> or ?key=) the limit follows your plan.
  • ssl.sh only connects to public addresses; private, loopback, link-local, CGNAT (100.64.0.0/10) and reserved ranges are refused with blocked_address.
  • Results are not published or indexed. Pages under /<host> carry noindex.

FAQ

Why do I get HTML instead of text?

ssl.sh returns plain text only to command-line user agents (curl, Wget, HTTPie, xh, python-requests, Go's net/http, aiohttp, libfetch). In Windows PowerShell curl is an alias for Invoke-WebRequest; run curl.exe ssl.sh/example.com instead, or add ?json to any URL.

How fresh are the results?

Results are cached for 5 minutes (failed checks for 1 minute). Add ?fresh=1 to force a new connection, for example right after you install a new certificate.

Sources

  1. RFC 5280: X.509 certificate and CRL profile
  2. RFC 8446: TLS 1.3
  3. RFC 6797: HTTP Strict Transport Security
  4. RFC 8659: DNS CAA resource record