TL;DR: Your cert is missing valid Signed Certificate Timestamps (SCTs), so it can’t prove it was published to public Certificate Transparency (CT) logs. The fastest fix is almost always to re-issue the cert from a CA that embeds SCTs by default (Let’s Encrypt, ZeroSSL, DigiCert, Sectigo, GlobalSign) and redeploy. If the error only appears on a corporate or school network, a TLS-inspection proxy is stripping the SCTs and the fix lives in that network’s policy, not on your origin.
Your cert chain looks clean. openssl verify passes, the chain is complete, the hostname matches, the expiry is well in the future. Yet Chrome refuses the connection with NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED. This is Certificate Transparency (CT): since 2018 Chrome requires that every publicly-trusted TLS cert prove inclusion in CT logs via SCTs. When the SCTs are missing, malformed, too few, from disqualified logs, or stripped in transit, Chrome rejects the cert outright with no user clickthrough.
One thing changed since this problem was first common: Firefox now enforces CT too. Desktop Firefox 135+ (shipped early 2025) and Firefox for Android 145+ reject CT-non-compliant certs from CAs in Mozilla’s root program, and Safari/Apple platforms have enforced CT for years. So “it works in Firefox” is no longer a reliable signal that the cert is fine. If your cert fails CT, expect it to fail in every mainstream browser eventually.
Private CAs and corporate/internal CAs are exempt from CT only if their roots are not in the browser’s default trust store. If your cert chains up to a “real” public root, CT is mandatory.
How many SCTs do you actually need? As of June 2026, Chrome’s CT policy requires (a) at least 2 SCTs for certs with a validity of 180 days or less, or at least 3 SCTs for certs valid longer than 180 days, embedded in the cert; and (b) those SCTs must come from at least 2 distinct log operators. A cert with only one SCT, or two SCTs from the same operator, fails.
Which bucket are you in?
Use this to jump to the right fix before reading every cause.
| What you observe | Most likely cause | Go to |
|---|---|---|
| Error on every network, your own public CA cert | CA never embedded SCTs / too few SCTs | Causes 2, 5 → Steps 1-2 |
| Cert chains to a public root but you issued it internally | Internal CA cross-signed to a public root | Cause 1 → Step 3 |
| Error only on corporate / school / VPN network | TLS-inspection proxy strips SCTs | Cause 6 → Step 5 |
| Error appeared suddenly on an unchanged cert | A CT log it relied on got distrusted | Cause 4 → Steps 1-2 |
| Error on a brand-new cert, clears in minutes | SCT propagation timing | Cause 5 → wait, then re-check |
openssl s_client shows SCTs but Chrome still rejects | SCTs from fewer than 2 distinct operators | Causes 4-5 → Step 2 |
Common causes
Ordered from most common in practice.
1. Internal CA cert without SCTs hits a Chrome-trusted root by accident
You issued a cert from your internal CA. The internal CA’s intermediate happens to chain up to a publicly-trusted root (because you cross-signed for convenience, or imported a Chrome-trusted root into the chain). Chrome treats it as a public cert and demands CT — but your internal CA doesn’t log to CT, so no SCTs.
How to spot it: openssl s_client -connect yourdomain.com:443 -servername yourdomain.com shows a chain that ends at a public root. The leaf has no SCT extension.
2. CA accidentally issued without embedding SCTs
A legitimate public CA had a bug or a manual workflow step missed CT logging. The cert is real but lacks SCTs. Rare with major CAs (Let’s Encrypt, DigiCert) but happens occasionally with smaller CAs or new products.
How to spot it: openssl x509 -in cert.pem -noout -text | grep -A 10 "CT Precertificate SCTs" shows nothing (that is the exact OpenSSL label for the embedded-SCT extension, OID 1.3.6.1.4.1.11129.2.4.2). A crt.sh search for your cert serial returns empty.
3. SCTs delivered via TLS extension but server doesn’t support it
CAs can deliver SCTs three ways: embedded in cert (X.509 extension), via TLS extension during handshake, or via OCSP stapling. If your CA chose TLS-extension delivery and your web server (older nginx, certain haproxy versions) doesn’t forward the extension, Chrome sees no SCTs.
How to spot it: Cert itself has no embedded SCT. openssl s_client -ct -connect yourdomain.com:443 shows no SCTs in handshake. Server version is old enough to predate the feature.
4. SCTs were valid at issuance but the log got distrusted
Chrome periodically retires CT logs (a log operator goes out of business, a log is found non-compliant, or the whole log type is being phased out). Certs whose qualifying SCTs all come from now-retired logs become CT-non-compliant retroactively. This is a live concern in 2026: the ecosystem is migrating from the original RFC 6962 logs to the newer “static-ct-api” (tiled) logs. Let’s Encrypt made its RFC 6962 logs read-only on 2025-11-30 and shut them down on 2026-02-28, writing only to static-ct-api logs afterward. From 2026-04-15 Chrome dropped its old rule that at least one SCT had to come from an RFC 6962 log, so SCTs from static-ct-api logs now count on their own.
How to spot it: Cert has embedded SCTs, but crt.sh shows the issuing log is no longer in Chrome’s current log list. A fresh cert from the same CA produces SCTs from current logs and validates fine.
5. Cert is brand new and the SCT timing window hasn’t lined up
CT requires SCTs from at least 2 logs operated by different organizations (and at least 2 or 3 SCTs total depending on validity period). A cert issued in the last few minutes may temporarily fail in some clients while the SCTs propagate, or a misconfigured issuance pipeline may embed only one SCT.
How to spot it: Cert issued within the last few minutes and the error clears on its own; or openssl shows SCTs but Chrome still rejects, meaning the SCTs come from too few distinct operators. Re-issuing fixes it immediately.
6. MitM proxy injecting cert breaks CT
Corporate TLS inspection proxies (Zscaler, Bluecoat, etc.) re-sign every cert with the corporate CA. The injected cert has no SCTs because the corporate CA doesn’t log to CT. Within Chrome’s enterprise policy this is allowed; outside it, it fails.
How to spot it: Issue is only seen on corporate / school networks. The cert in browser DevTools is issued by something like “Corporate Root CA”, not your real CA.
Before you start
- Confirm the user really is hitting CT error, not a generic cert error — the exact code
NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIREDis the only diagnostic for this. - Identify the CA that issued your cert and check its CT logging policy.
- Have the cert’s PEM file accessible — you’ll inspect it offline.
- Know whether the affected user is on a corporate/managed network with TLS inspection.
Information to collect
- The exact Chrome error code from the user.
- Output of
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts. - Output of
openssl x509 -in cert.pem -noout -text | grep -A 30 "CT". - Cert serial number — used to search crt.sh.
- The issuing CA and intermediate names.
- Chrome version on the affected client.
Step-by-step fix
Order: confirm CT is really the issue, then fix at issuance.
Step 1: Confirm the diagnosis with openssl and crt.sh
Pull the cert:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com </dev/null 2>/dev/null | openssl x509 -outform PEM > cert.pem
Check for embedded SCTs:
openssl x509 -in cert.pem -noout -text | grep -A 30 "CT Precertificate SCTs"
You want to see a list of SCTs, each with a Log ID, Timestamp, and Signature. Count them and count distinct logs: you need at least 2 SCTs (3 if the cert is valid longer than 180 days) from at least 2 different operators. Empty output means no embedded SCTs at all.
You can also read the SCTs straight off the live handshake without saving a file:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -text | grep -A 30 "CT Precertificate SCTs"
Cross-check on crt.sh:
https://crt.sh/?q=yourdomain.com
If your cert appears in crt.sh, it was logged. If not, the CA never published it.
Step 2: If cert lacks SCTs entirely, re-issue from a CA that logs reliably
The fastest fix is a fresh cert from Let’s Encrypt, ZeroSSL, or any major public CA. All of them embed the required SCTs at issuance now, and they write to current logs (Let’s Encrypt writes to static-ct-api logs as of 2026).
sudo certbot certonly --webroot -w /var/www/letsencrypt -d yourdomain.com
Inspect the new cert for embedded SCTs before you trust it:
openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -noout -text | grep -A 30 "CT Precertificate SCTs"
You should see at least two SCTs (three if you requested a long-lived cert) from at least two distinct logs. Deploy, reload the web server, and the error clears.
If you are on a paid CA, you usually do not even need a new CSR. The DigiCert, Sectigo, and GlobalSign consoles all have a Reissue action on the order; reissue and the new cert will carry embedded SCTs. Do not just re-download the same cert: if the original lacks SCTs, the re-download will too.
Step 3: If using internal CA, decide between two paths
Path A: Make the internal CA not chain to a public root. Then Chrome treats it as private, no CT required. Achieve this by:
- Using a self-signed root specific to your org.
- Distributing that root to client trust stores via MDM / group policy.
- Removing any cross-sign to a public root.
Path B: Use a real public cert (Let’s Encrypt with DNS-01 works even for internal-only domains, since the validation is DNS not HTTP).
Pick Path B unless your domain is on a private TLD (.internal, .corp) that isn’t in the public DNS.
Step 4: If server isn’t forwarding TLS-extension SCTs, upgrade or switch delivery mode
SCTs can be delivered three ways: embedded in the cert (an X.509 extension, by far the most common today), via a TLS handshake extension, or via OCSP stapling. If your CA uses TLS-extension or OCSP-stapling delivery, your server has to forward it, and many setups silently drop it.
- haproxy supports loading static SCT files alongside certs (
crt-listwith.sctl/.ocspcompanion files) for the TLS-extension path. - nginx and Apache have no first-class config for serving TLS-extension SCTs without a third-party module; the dependable approach there is embedded SCTs.
Easier path for almost everyone: ask the CA to switch the order to embedded delivery (SCTs baked into the cert extension) so the web server needs no special config. With Let’s Encrypt, ZeroSSL, and the standard products from DigiCert/Sectigo/GlobalSign, embedded is already the default, so a plain reissue (Step 2) usually solves this too.
Step 5: For corporate TLS-inspection issues, fix policy or accept the limitation
If users on a corporate network can’t reach your site due to TLS inspection breaking CT:
- Recommend the corporate IT team add your domain to a TLS-inspection bypass list.
- On managed devices, IT can also scope an exception with the
CertificateTransparencyEnforcementDisabledForCasorCertificateTransparencyEnforcementDisabledForUrlsenterprise policy so the inspection CA is trusted without CT. This is a managed-device-only setting; it does nothing for the general public. - This is not your problem to fix on your origin. Communicate the constraint; do not try to weaken your real cert to accommodate one network’s proxy.
Step 6: Add cert.sh / CT monitoring to catch future issues early
Use https://crt.sh/?q=yourdomain.com or set up an automated CT log watcher:
# pseudo-monitor: alert if a cert for our domain shows up in CT that we didn't issue
curl -s "https://crt.sh/?q=yourdomain.com&output=json" | jq '.[] | select(.entry_timestamp > "2026-05-01")'
This detects rogue cert issuance AND confirms your own certs are being logged.
How to confirm it’s fixed
openssl x509 -in cert.pem -noout -text | grep -A 30 "CT Precertificate SCTs"returns at least 2 SCTs (3 for long-lived certs) from at least 2 distinct logs.https://crt.sh/?q=yourdomain.comshows the new cert serial within minutes of issuance.- Chrome loads
https://yourdomain.comwithout error on the originally-affected client. - In Chrome, open DevTools → Security tab (or click the cert via the page-info button), and confirm the certificate is valid with no CT warning.
- Repeat in Firefox 135+ and Safari, since both now enforce CT. A pass in only one browser is not proof.
- Test from a clean Chrome incognito window on a non-corporate network, to rule out a cached error or a proxy-specific failure.
Long-term prevention
- Always use a CA known to embed SCTs: Let’s Encrypt, ZeroSSL, DigiCert, Sectigo, GlobalSign — all current.
- Never cross-sign your internal CA to a public root unless you intend to issue from the public root.
- Set up CT monitoring with
cert-spotter,crt.shwatchers, or commercial offerings (Hardenize, Censys) to detect missing or unexpected certs. - After any major Chrome release, sanity-check that none of your certs are on now-disqualified logs.
- Document your cert-issuance flow including SCT delivery mode so an unknown engineer can troubleshoot in a year.
Common pitfalls
- Assuming “the cert chains, therefore it’s valid.” CT is checked independently of chain validity.
- Treating a Firefox pass as proof. Firefox 135+ enforces CT now; an old Firefox loading the page just means that copy is stale.
- Re-downloading the same cert from your CA when the original lacks SCTs. You need a true reissue, not a re-download.
- Trying to disable CT user-side in Chrome. It only works via enterprise policy on managed devices, never for general visitors.
- Forgetting CT applies to ECDSA and RSA certs equally; the algorithm doesn’t matter.
- Not re-verifying SCTs after a CA migration or a log retirement. The new path might use TLS-extension delivery your server drops, or rely on a log that is no longer trusted.
FAQ
Q: It loads in Firefox but Chrome blocks it. Is the cert actually fine?
No longer a safe assumption. Firefox enforced CT starting with desktop 135 (early 2025) and Android 145, and Safari/Apple platforms have enforced it for years. If your cert fails CT, expect every mainstream browser to reject it sooner or later. If an old Firefox still loads it, that is a stale Firefox, not a healthy cert. Treat the Chrome error as the truth and fix the cert.
Q: How many SCTs does my cert need, exactly?
As of June 2026, Chrome wants at least 2 SCTs for a cert valid 180 days or less, or at least 3 SCTs if it is valid longer than 180 days, and those SCTs must come from at least 2 distinct log operators. A modern CA handles this automatically; you mostly hit trouble when an internal pipeline or an old log list is involved.
Q: Can I tell Chrome to skip CT for my domain?
Only via enterprise managed-Chrome policies (CertificateTransparencyEnforcementDisabledForUrls or ...DisabledForCas), which apply only to managed devices. There is no per-user opt-out for the general public, and you should not design around one.
Q: My CA says they DO log to CT but my cert has no SCTs. What’s going on?
Most likely the CA logs the cert but delivers SCTs via TLS extension or OCSP stapling instead of embedding them, and your web server drops that. Ask the CA to switch the order to embedded delivery (Step 4), then reissue. With Let’s Encrypt/ZeroSSL/DigiCert/Sectigo/GlobalSign, embedded is already the default.
Q: The error appeared on a cert that worked for months. Why now?
A CT log your cert relied on was distrusted or retired. This is common during the 2026 migration from RFC 6962 logs to static-ct-api logs. Reissue from your CA so the new cert carries SCTs from current logs.
Q: Does CT apply to wildcard and ECDSA certs?
Yes, identically. Wildcard certs follow the same issuance path and need qualifying SCTs, and CT enforcement does not care whether the cert is RSA or ECDSA.
Related: SSL cert delay, SSL mixed content warning, CAA record blocks cert issuance, SSL cert auto-renewal failed silently.
Tags: #Troubleshooting #SSL #certificate-transparency #chrome #Debug