# Check SSL certificates with curl: ssl.sh command line

> Run curl ssl.sh/example.com for a certificate summary, /days for days until expiry, /chain for the full chain PEM and /json for JSON. No install needed.

Source: https://ssl.sh/docs/cli/
Last updated: 2026-11-10

`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.

```bash
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):

```text
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](https://ssl.sh/errors/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`](devdoc:api)). |
| `/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.

```bash
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:

```bash
#!/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](https://ssl.sh/errors/missing-intermediate-certificate/):

```bash
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:

```bash
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`.
