Reference
Check SSL certificates with curl: ssl.sh command line
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>carrynoindex.
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.