# SSL Certificate Checker

> SSL certificate checker: test many domains for expiry, issuer, names, TLS version and chain. Optional CT history. No login or API key.

URL: https://cliqtomedia.com/actors/ssl-certificate-inspector/

Category: Developer & website monitoring · Status: coming soon · Apify profile: https://apify.com/cliqtomedia?fpr=cliqto

Check the SSL certificate of many sites in one run. For each host you get a clear verdict and the days left. You also get the issuer, names and TLS version.

## How to use it: 3 steps

1. **Follow us on Apify.** The tool is built and waits in the queue for the Apify Store. Apify is the cloud platform where our tools run.
2. **Paste your input and press Start.** No code needed: you fill in a form (the input). It already has a working example. For example: `apify.com` in Domains. Each start is one run.
3. **Download the results.** When the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.

## What it does

Check the SSL/TLS certificate of many websites in one run. For each host you get a clear verdict (valid, expiring soon, expired, wrong name or untrusted), the days left, issuer, all names on the certificate (SANs), TLS version and the certificate chain. Optional: certificates logged for the host in Certificate Transparency (crt.sh). No login and no API key.

- Trust comes from the standard list of trusted root certificates (the same check that makes a browser show a warning).
- Name match checks that the host name is on the certificate.
- Dates are compared with the time of the check. `daysLeft` is negative for an expired certificate.
- CT history (optional) comes from crt.sh, a free public search over Certificate Transparency logs.

### Good for

- Find certificates that expire soon across many client sites (`daysLeft`, `certStatus: expiring_soon`).
- Find broken HTTPS: expired, self-signed, wrong host name, missing intermediate (`certStatus`, `validationError`).
- Make an inventory of who issued your certificates and which names they cover (`issuer`, `sanList`).
- Check mail and admin ports over TLS, for example `mail.example.com:993` or `:8443`.
- See which certificates were issued for a name in the past (CT history).

### Not a good fit

- No revocation check. The Actor does not ask OCSP or CRL servers. A revoked certificate can still show `valid`.
- No protocol or cipher scan. You get the TLS version and cipher that the server chose. The Actor does not test every TLS version or cipher, and it does not give a security score.
- No STARTTLS. Ports that start in plain text (SMTP on 25/587, IMAP on 143) are not supported. Use the TLS ports (465, 993, 995).
- Public hosts only. Private, loopback, link-local and reserved addresses (for example `10.0.0.1`, `127.0.0.1`, `169.254.169.254`) are not checked.
- CT history is slow. crt.sh is a free community service. It answers in 5–20 seconds per host and allows about 5 requests a minute, so CT history is meant for short lists.

## Price

Prices will be shown after release.

## Questions

### Do I need an account or an API key?

No. It connects to each host directly. CT history uses the public crt.sh service.

### Why does a revoked certificate show valid?

It does not check revocation lists. It checks trust, name and dates only.

### Why is daysLeft null for some rows?

No certificate was read. The host does not exist or did not answer.

### Does it work with SMTP or IMAP on plain ports?

No. STARTTLS is not supported. Use TLS ports like 465, 993 or 995.

### Is CT history complete?

It is what crt.sh returns at run time. crt.sh covers the main public CT logs, but it can be slow, can time out, and may not show the newest certificates yet.

### I used another SSL checker. Are the field names the same?

Not exactly. The meaning maps like this: expiry days → `daysLeft`; expired → `certStatus: expired`; self-signed → `selfSigned`; chain valid → `trusted`; host name matches → `hostnameMatch`; TLS version → `protocol` (Node naming, for example `TLSv1.3`); issued by → `issuer`. Failed checks here give `null`, not `0` or empty strings.

## Data fields

- `input` (string): The value as given in the input.
- `domain` (string): Host that was checked (lowercase ASCII/punycode) or IP address.
- `port` (integer): TCP port of the TLS connection.
- `ipAddress` (string): Public IP address the connection was made to (IPv4 preferred).
- `checkStatus` (string): ok = a certificate was read (host check + certificate charged); not_found = the host name does not resolve in DNS (host check charged); error = no TLS answer: refused, timeout or no TLS on the port (host check charged), or a private address (not charged).
- `certStatus` (string): Verdict on the certificate the server sent: valid; expiring_soon (valid, fewer than warnDays days left); expired; not_yet_valid; hostname_mismatch (does not cover the host name); untrusted (chain does not verify against the Mozilla root store: self-signed, unknown root, incomplete chain). null when no certificate was read.
- `valid` (boolean): true when browsers would accept the certificate for this host today (certStatus valid or expiring_soon). null when no certificate was read. Revocation is not checked.
- `trusted` (boolean): true when the chain verifies against the Mozilla root store; false when it does not; null when it was not determined (the certificate is expired or not yet valid, so verification stopped at the date).
- `hostnameMatch` (boolean): true when the certificate covers the checked host name (SAN or wildcard).
- `expiringSoon` (boolean): true when certStatus is expiring_soon: the certificate is accepted today but has fewer than warnDays days left. false for expired, mismatched or untrusted certificates (their certStatus already names the problem).
- `daysLeft` (integer): Whole days until expiry (rounded down). 0 = expires within 24 hours; negative = already expired by that many days.
- `validFrom` (string): Start of validity (notBefore), ISO 8601 UTC.
- `validTo` (string): Expiry (notAfter), ISO 8601 UTC.
- `validationError` (string): OpenSSL verification code when the certificate is not accepted, e.g. CERT_HAS_EXPIRED, ERR_TLS_CERT_ALTNAME_INVALID, UNABLE_TO_VERIFY_LEAF_SIGNATURE.
- `issuer` (string): Issuing CA common name (or organization), e.g. R11.
- `issuerOrganization` (string): Issuing CA organization, e.g. Let's Encrypt.
- `subject` (string): Certificate subject common name (or organization).
- `subjectOrganization` (string): Subject organization (OV/EV certificates).
- `sanList` (array): Subject Alternative Names: host names and IPs the certificate covers (up to 500).
- `sanCount` (integer): Number of Subject Alternative Names.
- `serialNumber` (string): Serial number (hex).
- `fingerprintSha256` (string): SHA-256 fingerprint of the certificate.
- `keyType` (string): Public key type, e.g. RSA, EC prime256v1.
- `keyBits` (integer): Public key size in bits.
- `selfSigned` (boolean): Subject equals issuer.
- `protocol` (string): Negotiated TLS version, e.g. TLSv1.3.
- `cipher` (string): Negotiated cipher suite.
- `legacyTlsOnly` (boolean): true when the server only completed a handshake with TLS 1.0/1.1 or weak ciphers allowed.
- `chain` (array): Certificates the server sent, leaf first, followed to the root the verifier found. null when includeChain is off or no certificate was read.
- `chainLength` (integer): Number of certificates in the chain.
- `handshakeMs` (integer): Time to complete the TLS handshake, ms.
- `ctStatus` (string): Certificate Transparency lookup on crt.sh, made for hosts whose certificate was read: found (charged), none (crt.sh lists no certificate for this exact name), unavailable (crt.sh did not answer; not charged), skipped (includeCtHistory off, IP address, no certificate was read, or the ctMaxHosts limit was reached - then ctError says so).
- `ctCertificatesFound` (integer): Unique certificates crt.sh lists for this exact name (before the ctHistoryLimit cut). null when not looked up or unavailable.
- `ctHistory` (array): Newest certificates logged for this exact name, up to ctHistoryLimit. Wildcard certificates of the parent domain are not included.
- `ctError` (string): Why CT history is missing, when ctStatus is unavailable.
- `error` (string): Why no certificate was read (DNS, connection or TLS failure). Never contains credentials.
- `errorCode` (string): Machine-readable failure code: DNS_NOT_FOUND (name does not exist), DNS_ERROR (name servers do not answer: SERVFAIL, tried 3 times), BLOCKED_ADDRESS, CONNECTION_REFUSED, CONNECTION_TIMEOUT, CONNECTION_RESET, NETWORK_UNREACHABLE, TLS_HANDSHAKE_FAILED, NO_CERTIFICATE, TECHNICAL_ERROR.
- `checkedAt` (string): When the check started, ISO 8601 UTC.
- `source` (string): Where the data came from.

## Input settings

- `domains` (array, required): Up to 5,000 host names, host:port values, URLs or public IP addresses, one per line (e.g. apify.com, mail.example.com:993, https://shop.example.org/page). Each host name is checked as given (www.example.com and example.com are different checks). Duplicates are checked once; invalid values are listed in RUN_SUMMARY and not charged.
- `port` (integer): Port used for values without their own port. 443 for HTTPS; 993/995/465 for mail over TLS, 8443 for admin panels.
- `warnDays` (integer): A valid certificate with fewer days left than this gets certStatus expiring_soon and expiringSoon true, and is listed in RUN_SUMMARY.attention.
- `includeChain` (boolean): Add the chain (intermediates and root) with subject, issuer, dates and fingerprints. Off keeps rows smaller; chainLength is always filled.
- `includeCtHistory` (boolean): Add certificates logged for each host name on crt.sh (issuer, dates, names, serial). crt.sh is a free community service and answers slowly (often 5-20 s per host); the Actor asks it one host at a time, so turn this on for lists of up to a few hundred hosts. Charged only when crt.sh lists certificates.
- `ctHistoryLimit` (integer): Newest logged certificates kept per host when CT history is on. ctCertificatesFound always shows the full count.
- `ctMaxHosts` (integer): crt.sh answers slowly and allows about 5 requests a minute, so CT history is looked up one host at a time for the first N certificates read. Other hosts get the full TLS check without CT (not charged for CT). 50 hosts need about 11 minutes; raise the run timeout for more.
- `connectTimeoutSecs` (integer): How long to wait for a TLS answer from each host before marking it unreachable (3-15 s).

## Step by step on Apify

1. Open the Input tab.
2. Put your hosts in Domains (`domains`), one per line. The field is prefilled with `apify.com` and `expired.badssl.com`.
3. Keep the defaults: port 443, chain on, CT history off, warning at 30 days.
4. Click Start.
5. Open the Output tab. The Dataset has four views:
6. Certificates — verdict, days left, expiry date, issuer;
7. Certificate details — SANs, key, fingerprint, cipher;
8. Chain — the intermediate and root certificates;

## Use cases

| Task | Input | Key fields | Next step |
|---|---|---|---|
| Expiry watch for client sites | `domains`, `warnDays` | `certStatus`, `daysLeft`, `validTo` | Sort by `daysLeft`; run on a schedule |
| Find broken HTTPS | `domains` | `certStatus`, `trusted`, `hostnameMatch`, `validationError` | Filter rows where `valid` is `false` |
| Certificate inventory | `domains`, `includeChain` | `issuer`, `issuerOrganization`, `sanList`, `keyType` | Export to CSV or Excel |
| Mail and admin ports | `domains` with `host:port`, or `port` | `certStatus`, `protocol`, `daysLeft` | Same as above |
| Issuance history of a name | `includeCtHistory: true` | `ctCertificatesFound`, `ctHistory` | Look for unknown issuers |

## Tips

- Run it every week and look for expiring_soon in certStatus.
- Add host:port values to check mail ports like mail.example.com:993.
- Turn on CT history to see old certificates for a name.

## Limits



- Up to 5,000 values per run (counted before duplicates are removed). For more, split the list.
- Measured on 256 MB: 500 hosts in about 1 minute, 5,000 hosts in about 7 minutes.
- A host that does not answer takes up to `connectTimeoutSecs` (default 10 s).
- CT history adds about 13 seconds per host (50 hosts: about 11 minutes).
- If more than 250 hosts cannot be reached and they are more than 10 times the certificates read, the run stops early with `TOO_MANY_FAILURES`. This protects you from paying for a wrong list. Names missing from DNS do not count here: they are a normal `not_found` result.
- The default run timeout is 1 hour. When the time runs out, the Actor saves what it has and marks the run `PARTIAL` with `DEADLINE`.

## Sample result

```json
{
  "input": "apify.com",
  "domain": "apify.com",
  "port": 443,
  "ipAddress": "3.170.42.100",
  "checkStatus": "ok",
  "certStatus": "valid",
  "valid": true,
  "trusted": true,
  "hostnameMatch": true,
  "expiringSoon": false,
  "daysLeft": 91,
  "validFrom": "2026-07-03T00:00:00.000Z",
  "validTo": "2027-01-16T23:59:59.000Z",
  "validationError": null,
  "issuer": "Amazon RSA 2048 M04",
  "issuerOrganization": "Amazon",
  "subject": "*.apify.com",
  "sanList": ["*.apify.com", "apifier.com", "*.apifier.com", "apify.com"],
  "sanCount": 4,
  "keyType": "RSA",
  "keyBits": 2048,
  "selfSigned": false,
  "protocol": "TLSv1.3",
  "cipher": "TLS_AES_128_GCM_SHA256",
  "legacyTlsOnly": false,
  "chain": [
    {
      "subject": "*.apify.com",
      "issuer": "Amazon RSA 2048 M04",
      "validTo": "2027-01-16T23:59:59.000Z",
      "daysLeft": 91,
      "isCa": false
    }
  ],
  "chainLength": 4,
  "ctStatus": "skipped",
  "ctHistory": null,
  "error": null,
  "errorCode": null,
  "checkedAt": "2026-10-17T09:19:05.357Z",
  "source": "Direct TLS handshake"
}
```

## Similar tools

- [Link Route Audit — Bulk HTTP Status & Redirects](https://cliqtomedia.com/actors/bulk-url-status-redirect-checker/)
- [GitHub Releases Monitor & Scraper](https://cliqtomedia.com/actors/github-releases-monitor-scraper/)
- [Software End-of-Life Checker](https://cliqtomedia.com/actors/software-end-of-life-checker/)
- [Dependency Vulnerability Scanner (OSV)](https://cliqtomedia.com/actors/osv-dependency-vulnerability-scanner/)
- [GitHub Repositories & Users Search (No Login)](https://cliqtomedia.com/actors/github-search/)
- [OpenAPI Catalog & Breaking Change Detector](https://cliqtomedia.com/actors/openapi-spec-catalog-diff/)
