Short answer. Certificate Transparency (CT, RFC 6962) requires CAs to submit every publicly trusted TLS certificate to public, append-only logs. Each log returns a signed promise to include the certificate, the SCT, and the CA embeds the SCTs in the certificate. Chrome, Safari and Firefox reject publicly trusted certificates without enough SCTs from enough independent logs. For you that means two things. Anyone can see every certificate issued for your domains, through Cert Spotter, crt.sh or your own monitor. And you should watch those logs, because a certificate you didn't request is the earliest sign of a DNS takeover, a compromised account or a misbehaving CA.
How CT works
A CT log is a Merkle tree, the same append-only hash structure Git and Bitcoin use. Anyone can download it, check that entries are never removed or changed, and get a short proof that a given certificate is included. Issuance with CT works like this:
- The CA builds a precertificate: the final certificate's contents plus a critical "poison" extension
(OID
1.3.6.1.4.1.11129.2.4.3), so it cannot be used as a real certificate. - It submits the precertificate to several logs. Each log returns a Signed Certificate Timestamp (SCT), a signature over the entry and a timestamp that promises inclusion within the log's Maximum Merge Delay (MMD).
- The CA issues the final certificate with the SCTs embedded in an X.509 extension (OID
1.3.6.1.4.1.11129.2.4.2). - Browsers check the SCT signatures against their list of trusted logs and count them against their policy.
- Monitors read the logs and report certificates for the names they watch. Auditors check that logs stay consistent and keep their promises.
The precertificate and the final certificate are both logged in most cases, which is why search tools often show each certificate twice.
The SCTs in a real certificate
openssl x509 -text decodes the embedded SCTs. This is the certificate letsencrypt.org served on 20 October
2026:
subject=CN=letsencrypt.org
issuer=C=US, O=Let's Encrypt, CN=YE2
notBefore=Sep 4 14:34:32 2026 GMT
notAfter=Dec 3 14:34:31 2026 GMT
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : D8:09:55:3B:94:4F:7A:FF:C8:16:19:6F:94:4F:85:AB:
B0:F8:FC:5E:87:55:26:0F:15:D1:2E:72:BB:45:4B:14
Timestamp : Sep 4 15:33:02.377 2026 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:46:02:21:00:D0:2E:25:A4:9D:7F:C8:18:D6:7B:5E:...
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : 46:AF:86:3D:3B:3E:E5:9F:A5:77:DE:A8:24:5D:36:B0:
D9:ED:22:A2:23:F4:61:77:41:22:94:52:EE:95:50:5F
Timestamp : Sep 4 15:33:02.552 2026 GMT
Extensions: 00:00:05:00:24:DF:DB:0C
Signature : ecdsa-with-SHA256
30:44:02:20:31:2F:83:D6:42:65:26:0D:58:D7:DD:C3:...
The log ID is the SHA-256 hash of the log's public key. Looking both IDs up in Chrome's log list
(log_list.json, version of 19 October 2026) gives:
| Log ID (base64) | Log | Operator | Type | State |
|---|---|---|---|---|
2AlVO5RPev/IFhlvlE+Fq7D4/F6HVSYPFdEucrtFSxQ= | Xenon2026h2 | RFC 6962, MMD 24 h | usable | |
Rq+GPTs+5Z+ld96oJF02sNntIqIj9GF3QSKUUu6VUF8= | Tuscolo2026h2 | Geomys | static-ct-api, MMD 60 s | usable |
Two logs from two different operators, the minimum for a 90-day certificate. The second SCT's extension is not
noise. Logs that follow the static-ct-api put the entry's position in the log
into the SCT: extension type 0 (leaf_index), length 5, and a 40-bit index, here 618,650,380. A monitor can
fetch that entry directly instead of searching for it.
The SCT decoder does the lookup for you. It shows each log's name, its state in Chrome's and Apple's lists, and whether the set satisfies each browser's policy. The certificate decoder shows the SCT extension together with the rest of the certificate.
SCTs can reach the browser in three ways:
- Embedded in the certificate, as above. This is what almost all CAs do, and it needs nothing from you.
- In the TLS extension
signed_certificate_timestamp. The server sends SCTs it obtained itself. Few servers support it. - Stapled in an OCSP response. Chrome accepted these only "in versions prior to version 148", and with OCSP being retired by Let's Encrypt and others, this route is fading (see OCSP and CRL in 2026).
What browsers require
All three major engines enforce CT for certificates from CAs in their public root programs.
| Chrome | Apple (Safari, iOS, macOS) | Firefox | |
|---|---|---|---|
| Embedded SCTs, lifetime ≤ 180 days | 2 from distinct logs | 2 | 2 from distinct logs |
| Embedded SCTs, longer lifetime | 3 from distinct logs | 3 (181–398 days) | 3 from distinct logs |
| Operator diversity | At least 2 distinct log operators | At most 1 (≤ 180 days) or 2 (longer) SCTs credited per operator | At least 2 distinct log operators |
| Other conditions | At least one embedded SCT from a log that is currently Qualified, Usable or ReadOnly | At least one embedded SCT from a currently approved log. At least one SCT from an RFC 6962 log | At least one SCT from a currently admissible log. The log list comes from Chromium's |
| SCTs via TLS or OCSP | 2 from currently trusted logs, 2 operators. OCSP only before Chrome 148 | 2 from currently approved logs, one of them delivered via TLS or OCSP | 2 from admissible logs, 2 operators |
| Error when not met | NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED | Connection fails | MOZILLA_PKIX_ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY |
Sources: the Chrome CT policy,
Apple's CT policy (April 2025), and Mozilla's
CT page. Firefox has enforced CT on
desktop since version 135 (February 2025) and on Android since version 145 (November 2025), according to
MDN. The pref
security.pki.certificate_transparency.mode shows the mode: 2 means enforced.
Two practical consequences:
- Browsers stop enforcing CT if their log list is too old. Chrome enforces CT only while its newest log list is less than 70 days old, and Firefox's list expires after 10 weeks. A machine that hasn't updated for months does not enforce CT at all, so don't use one to test CT failures.
- Private CAs are exempt. Chrome's enterprise notes say CT "only applies to CAs that are publicly-trusted". Certificates from a root you installed yourself need no SCTs, and logs will not accept them anyway.
The logs themselves
Chrome's log list of 19 October 2026 counts 21 usable RFC 6962 logs, plus 2 read-only and 3 retired ones. It
also lists 22 usable and 37 qualified static-ct-api logs. Eleven operators run logs that are usable or
qualified: Cloudflare, DigiCert, Geomys, GoDaddy, Google, HARICA, IPng Networks, Let's Encrypt, Microsec,
Sectigo and TrustAsia. Logs are sharded by time. A log named 2026h2 accepts only certificates that expire in
the second half of 2026, which keeps each log's size bounded and lets it be retired when its certificates have
expired.
Both kinds of logs accept submissions through the same RFC 6962 endpoints (add-chain, add-pre-chain). They
differ in how monitors read them. An RFC 6962 log answers dynamic API calls:
{"tree_size":2980148930,"timestamp":1792499093002,"sha256_root_hash":"wr3F5apMl2E7wQHqbAA1yKsqaZbAzBXTT8WTAeRAy8M=","tree_head_signature":"BAMARzBFAiEAsC3Pr7fsPwy0vchrMDIXka7HxrZANu5tcXclDCUqqSYCIGJ64SaIT0tjM5hCGfycNGPfu6FP8q8RvW14Q4SoyvVU"}
That is close to three billion entries in one half-year shard. A static-ct-api ("tiled") log publishes its signed tree head as a plain-text checkpoint, and its entries as immutable 256-entry tiles that CDNs can cache:
tuscolo2026h2.sunlight.geomys.org
1007870339
aRhh7dPjYarH0itUM1LD+XJw7CW41z1+9tSnf3AizuY=
— tuscolo2026h2.sunlight.geomys.org W+DWDWi4dqCfFx8da0IS4+jwHKVL6ETEHGQCHn5y8rKflUhLRtLHQ3c6...
— grease.invalid Aui1jDc8Y7Uve7seHd/Pg1p+wHSUmw0+MTNyEKEAAbHaE5bY3Gi/MUZ3zBuk+NxUyhJL...
— tuscolo2026h2.sunlight.geomys.org 7BiSLwAAAaFZRnbMBAMARzBFAiA++3wWdtsK2H8l3IxZh6H/OQml...
The first lines are the origin, the tree size and the root hash. The lines starting with "—" are signatures.
Note that the monitoring URL (skylight) differs from the submission URL (sunlight). Both are listed in the
log list.
Finding your certificates
Two public search services cover practically all logs.
Cert Spotter API (SSLMate). The
issuances endpoint lists unexpired certificates for a
domain. It works without a key for a limited number of queries per hour. For production use SSLMate asks you
to sign up for a key. Here are the certificates for ecdsa.com:
curl -s "https://api.certspotter.com/v1/issuances?domain=ecdsa.com&include_subdomains=true&expand=dns_names&expand=issuer"
{
"id": "16609031524",
"tbs_sha256": "8c6f1f0bef5434da2012d5f29eab13f00bcec659f17832c07872ff600f61ecf5",
"cert_sha256": "1396447e3454a081fe4e48dc1d5de18b7e3cf857ec1d7ff3c987d091c9024b04",
"dns_names": [
"ecdsa.com"
],
"pubkey_sha256": "9a1fa381dad4630b83cf3a1a1a7a3da7f04577db8a41933dd9bac49dabfa8c29",
"issuer": {
"friendly_name": "Let's Encrypt",
"pubkey_sha256": "2e8307068b6db620e4a39d068b5dee5d6ef5788cbb2c0b6d23ead84fcc17178c",
"name": "C=US, O=Let's Encrypt, CN=YR1"
},
"not_before": "2026-08-19T14:52:58Z",
"not_after": "2026-11-17T14:52:57Z",
"revoked": false
}
Summarized, the response held five issuances:
16609031524 2026-08-19 2026-11-17 Let's Encrypt ecdsa.com
16609045002 2026-08-19 2026-11-17 Let's Encrypt ecdsa.com,www.ecdsa.com
17525925247 2026-10-01 2026-12-30 Let's Encrypt *.ecdsa.com,ecdsa.com
17875915585 2026-10-19 2027-01-17 Let's Encrypt ecdsa.com,www.ecdsa.com
17878726033 2026-10-20 2027-01-18 Let's Encrypt ecdsa.com
Results come in the order SSLMate discovered them. To page through, pass the last id as after=. An empty
array comes with a Retry-After header, which makes the endpoint usable as a polling feed. Note what the
pubkey_sha256 field lets you do. Comparing it with your servers' keys tells you which certificates are
actually deployed and which were issued and never used.
crt.sh (Sectigo) has a web UI and a JSON output (https://crt.sh/?q=example.com&output=json). It is free
and covers a very long history, including expired certificates. It is also a busy shared service. All four of
our JSON queries on 20 October 2026 returned 502 Bad Gateway. Treat it as a convenience, not as a
dependency of your monitoring.
The CT history tool shows a domain's certificates over time, with the issuer, validity and names of each.
Monitoring your domains
Search answers "what exists?". Monitoring answers "what is new?" and alerts you within minutes or hours of issuance. Options:
- A hosted monitor. SSLMate's hosted Cert Spotter and the dashboards of several CAs send email or webhooks for new certificates on your domains.
- The open-source
certspotter(v0.25.0, September 2026) reads the logs itself. You list names in~/.certspotter/watchlist(a leading dot covers all subdomains, e.g..example.com), and it emails you or runs scripts from~/.certspotter/hooks.dfor each match. Since v0.24 it also hascertspotter-authorize, which marks expected certificates so only the unexpected ones alert. - The search API as a feed. Poll the Cert Spotter issuances endpoint with
after=and compare each new certificate with what your automation issued.
What to alert on:
- An issuer you don't use. Match against your CAA records. A certificate from a CA that your CAA forbids is a CA incident and must be reported to the CA.
- Names you don't recognise, especially forgotten subdomains. A certificate for
old-campaign.example.comcan mean a dangling DNS record was taken over. - A public key you didn't generate for a name your automation manages.
- Wildcards if your policy does not use them.
With 47-day certificates, legitimate issuance becomes frequent. Without
certspotter-authorize or a comparison against your ACME client's records, alert fatigue will make you ignore
the one certificate that matters.
CT and private names
Everything in a publicly trusted certificate is published: every name in the SAN list, forever. Internal host
names like jenkins.corp.example.com or staging-payments.example.com become public the moment you get a public
certificate for them, and anyone who reads the logs, including people scanning for targets, sees them. Options,
from Chrome's enterprise notes and common practice:
- Use a private CA for internal names. CT does not apply, and nothing is published.
- Use a wildcard certificate (
*.corp.example.com) when a public certificate is unavoidable. It reveals the zone but not each host. - Don't rely on asking a public CA not to log. The certificate would then fail in every CT-enforcing browser.
Common mistakes
- Thinking an SCT proves inclusion. An SCT is a promise to include the entry within the MMD. Auditors and monitors check that logs keep it.
- Counting duplicates. Precertificate and certificate entries for the same issuance appear as two rows in most search tools. They share the issuer and serial number, so deduplicate on that pair before you count certificates.
- Expecting revocation information in CT. A revoked certificate stays in the log forever. Revocation is a separate system.
- Using an old browser to test enforcement. CT enforcement turns itself off when the browser's log list is stale.
- Monitoring through one free service only. crt.sh outages happen. Use a second source or your own monitor.
Sources
- RFC 6962: Certificate Transparency (precertificates, SCTs, log API). RFC 9162 defines CT 2.0, which browsers do not use.
- static-ct-api: the tiled log read API and the
leaf_indexextension. - Chrome CT policy, Chrome log list, CT for enterprises.
- Apple's Certificate Transparency policy and log list.
- MDN: Certificate Transparency, Mozilla's Certificate Transparency page and the Firefox 135 release notes.
- SSLMate, Cert Spotter API and certspotter on GitHub.
Command output was produced on 20 October 2026 with OpenSSL 3.5.7 and curl 8.14.1 in node:24.22.0-trixie.