一句话先说结论: 你的证书缺少有效的 Signed Certificate Timestamps(SCT),无法证明自己已发布到公开的 Certificate Transparency(CT)log。最快的修法几乎总是从一家默认 embed SCT 的 CA 重签证书(Let’s Encrypt、ZeroSSL、DigiCert、Sectigo、GlobalSign),再重新部署。如果错误只在企业网或校园网出现,那是 TLS 检查代理把 SCT 剥掉了,要修的是那个网络的策略,不是你的源站。
证书链看着干净:openssl verify 过、链完整、主机名匹配、过期日期还远。但 Chrome 拒绝连接,报 NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED。这是 Certificate Transparency(CT):从 2018 年起 Chrome 要求每张公开受信任的 TLS 证书通过 SCT 证明自己已被 CT log 收录。SCT 缺失、损坏、数量不够、来自被取消资格的 log、或者在传输途中被剥掉,Chrome 就直接拒绝、没有用户点穿。
有一点和这个问题最初常见时不一样了:Firefox 现在也强制 CT 了。 桌面版 Firefox 135+(2025 年初发布)和安卓版 Firefox 145+ 会拒绝来自 Mozilla root 计划内 CA 的不合规证书,Safari / Apple 平台更是强制 CT 好多年了。所以”Firefox 能开”已经不是证书没问题的可靠信号。证书过不了 CT,迟早所有主流浏览器都会拒。
私有 CA 和企业 / 内部 CA 只在 它们的 root 不在浏览器默认信任库时才豁免 CT。证书一旦链到了”真”的公共 root,CT 就是强制的。
到底需要几条 SCT?截至 2026 年 6 月,Chrome 的 CT 策略要求:(a) 有效期不超过 180 天的证书至少 2 条 SCT,或有效期超过 180 天的证书至少 3 条 SCT,embed 在证书里;并且 (b) 这些 SCT 必须来自至少 2 家不同的 log 运营方。只有 1 条 SCT、或者 2 条 SCT 来自同一家运营方的证书都会失败。
你属于哪一类?
读完所有原因之前,用这张表直接跳到对应的修法。
| 你看到的现象 | 最可能的原因 | 去哪一步 |
|---|---|---|
| 任何网络都报错、用的是你自己的公共 CA 证书 | CA 没 embed SCT / SCT 数量不够 | 原因 2、5 → 第 1-2 步 |
| 证书链到了公共 root、但其实是你内部签的 | 内部 CA 交叉签到了公共 root | 原因 1 → 第 3 步 |
| 只在企业 / 校园 / VPN 网络报错 | TLS 检查代理剥掉了 SCT | 原因 6 → 第 5 步 |
| 没改动的证书突然开始报错 | 它依赖的某个 CT log 被取消信任 | 原因 4 → 第 1-2 步 |
| 刚签的证书报错、几分钟后自己好 | SCT 传播时间差 | 原因 5 → 等一会儿再查 |
openssl s_client 能看到 SCT、但 Chrome 还是拒 | SCT 来自不足 2 家运营方 | 原因 4-5 → 第 2 步 |
常见原因
按实际频率排序。
1. 内部 CA 证书没 SCT,但意外链上了 Chrome 信任的 root
你用内部 CA 签了证书。内部 CA 的中间证书恰好链到一个公开受信任的 root(因为方便交叉签了、或者把 Chrome 信任的 root 导进了链)。Chrome 把它当公共证书要求 CT——但内部 CA 不往 CT log 写,没 SCT。
怎么判断:openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 显示链终止在公共 root。Leaf 证书没 SCT 扩展。
2. CA 失误,签发时没 embed SCT
正规公共 CA 出 bug 或人工流程漏了 CT log 步骤。证书是真的、但缺 SCT。大 CA(Let’s Encrypt / DigiCert)罕见,小 CA 或新产品偶发。
怎么判断:openssl x509 -in cert.pem -noout -text | grep -A 10 "CT Precertificate SCTs" 啥都没有(这是 OpenSSL 给 embed SCT 扩展的确切标签,OID 1.3.6.1.4.1.11129.2.4.2)。crt.sh 用序列号搜不到。
3. SCT 通过 TLS 扩展送、但 server 不支持
CA 送 SCT 有三种方式:embed 在证书里(X.509 扩展)、TLS 握手扩展、OCSP stapling。CA 选了 TLS 扩展、但你 web server(旧版 nginx、某些 haproxy)不转发,Chrome 就看不到 SCT。
怎么判断:证书本身没 embed SCT。openssl s_client -ct -connect yourdomain.com:443 握手里看不到 SCT。Server 版本老到没这个功能。
4. SCT 签发时有效、但 log 后来被取消资格
Chrome 会周期性 retire CT log(运营方倒闭、log 被发现不合规、或者整类 log 在被淘汰)。合格 SCT 全都来自已退役 log 的证书会追溯性失去 CT 合规。这在 2026 年是个现实问题:整个生态正在从最初的 RFC 6962 log 迁移到更新的 “static-ct-api”(tiled)log。Let’s Encrypt 在 2025-11-30 把自己的 RFC 6962 log 设为只读、2026-02-28 彻底关停,之后只写 static-ct-api log。从 2026-04-15 起 Chrome 取消了”至少一条 SCT 必须来自 RFC 6962 log”的旧规定,所以单凭 static-ct-api log 的 SCT 现在也算数。
怎么判断:证书有 embed 的 SCT,但 crt.sh 显示发证 log 已不在 Chrome 当前 log 列表。同一家 CA 新签一张证书、SCT 来自当前 log,就能正常通过。
5. 证书刚签出来,SCT 时间窗还没对上
CT 要求 SCT 来自至少 2 家不同组织运营的 log(且按有效期总共要 2 或 3 条)。刚签出来几分钟的证书,可能在 SCT 传播期间临时失败;也可能是签发流程配错、只 embed 了一条 SCT。
怎么判断:证书几分钟前刚签、错误自己消失;或者 openssl 能看到 SCT 但 Chrome 还是拒,说明 SCT 来自的运营方不够。重新签一张立刻好。
6. MitM 代理注入证书破坏 CT
企业 TLS 检查代理(Zscaler / Bluecoat 等)把每张证书重新用企业 CA 签。注入的证书没 SCT,因为企业 CA 不往 CT log 写。Chrome 企业策略下允许、企业策略外失败。
怎么判断:问题只在企业 / 学校网出现。浏览器 DevTools 里证书 issuer 是类似 “Corporate Root CA”、不是你真正的 CA。
开始前
- 确认用户真的是 CT 错误,不是普通证书错误——
NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED这个具体错误码是唯一诊断信号。 - 找到签证书的 CA、看它的 CT 日志策略。
- 准备证书的 PEM 文件——要离线检查。
- 看清楚受影响用户是不是在带 TLS 检查的企业 / 校园网。
需要收集的信息
- 用户那边看到的 Chrome 错误码。
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts输出。openssl x509 -in cert.pem -noout -text | grep -A 30 "CT"输出。- 证书序列号——用来在 crt.sh 搜索。
- Issuer CA 和中间证书名。
- 受影响客户端的 Chrome 版本。
一步步修
顺序:先确认 CT 真的是问题,再在签发侧修。
第 1 步:用 openssl 和 crt.sh 确认诊断
抓证书:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com </dev/null 2>/dev/null | openssl x509 -outform PEM > cert.pem
查 embed 的 SCT:
openssl x509 -in cert.pem -noout -text | grep -A 30 "CT Precertificate SCTs"
要看到一份 SCT 列表,每条带 Log ID、Timestamp、Signature。数一下条数、再数有几家不同的 log:至少要 2 条 SCT(有效期超过 180 天则要 3 条)、来自至少 2 家不同运营方。输出为空 = 完全没 embed SCT。
也可以直接从实时握手里读 SCT、不用先存文件:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -text | grep -A 30 "CT Precertificate SCTs"
到 crt.sh 交叉验证:
https://crt.sh/?q=yourdomain.com
证书出现 = 进了 log。没出现 = CA 没发布。
第 2 步:证书完全没 SCT,换 CA 重签
最快的修法是从 Let’s Encrypt / ZeroSSL / 任意主流公共 CA 重签。他们现在都在签发时 embed 所需的 SCT,而且写的是当前的 log(Let’s Encrypt 在 2026 年写的是 static-ct-api log)。
sudo certbot certonly --webroot -w /var/www/letsencrypt -d yourdomain.com
信任新证书之前先查 embed SCT:
openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -noout -text | grep -A 30 "CT Precertificate SCTs"
应该至少看到 2 条 SCT(要的是长有效期证书则 3 条)、来自至少 2 家不同的 log。部署、reload web server,错误就清掉。
如果用的是付费 CA,通常连新 CSR 都不用。DigiCert、Sectigo、GlobalSign 的控制台都在订单上有 Reissue(重签)按钮;重签一次新证书就会带上 embed 的 SCT。别只是把同一张证书重新下载一遍:原件缺 SCT,重新下载的也缺。
第 3 步:用的是内部 CA,选两条路
路径 A:让内部 CA 不链到公共 root。这样 Chrome 当私有 CA 处理、不要求 CT。怎么做:
- 用专属于你公司的自签 root。
- 通过 MDM / 组策略分发这个 root 到客户端信任库。
- 移除任何到公共 root 的交叉签名。
路径 B:用真公共证书(Let’s Encrypt 加 DNS-01 challenge 对内部域名也能用,因为验证靠 DNS、不靠 HTTP)。
除非域名在私有 TLD(.internal / .corp、不在公网 DNS),都选 B。
第 4 步:Server 不转发 TLS 扩展 SCT,升级或换送达方式
SCT 有三种送达方式:embed 在证书里(X.509 扩展,今天绝大多数都是这种)、TLS 握手扩展、OCSP stapling。如果你的 CA 用 TLS 扩展或 OCSP stapling 送,就得靠你的 server 转发,而很多配置会把它静默丢掉。
- haproxy 支持在证书旁加载静态 SCT 文件(
crt-list配.sctl/.ocsp伴随文件)走 TLS 扩展这条路。 - nginx 和 Apache 没有现成配置能在不用三方模块的情况下提供 TLS 扩展 SCT;这两者上靠谱的做法就是用 embed 的 SCT。
对绝大多数人更简单:让 CA 把订单的送达方式切到 embedded(SCT 烤进证书扩展),web server 啥都不用配。Let’s Encrypt、ZeroSSL,以及 DigiCert / Sectigo / GlobalSign 的标准产品,embedded 本来就是默认,所以一次普通重签(第 2 步)通常就把这个问题一起解决了。
第 5 步:企业 TLS 检查问题:修策略 / 接受限制
企业网用户因为 TLS 检查打不开你的站点:
- 建议企业 IT 团队把你的域名加到 TLS-inspection bypass list。
- 在受管设备上,IT 也可以用
CertificateTransparencyEnforcementDisabledForCas或CertificateTransparencyEnforcementDisabledForUrls企业策略圈出例外,让检查用的 CA 不走 CT 也受信任。这是仅对受管设备生效的设置,对普通公众毫无作用。 - 这其实不是你源站要修的。把约束讲清楚,别为了迁就某个网络的代理去削弱自己真正的证书。
第 6 步:加 crt.sh / CT 监控提前发现问题
用 https://crt.sh/?q=yourdomain.com,或自动化 CT log 监听:
# 伪监控:发现 CT 里出现我们没签的证书就告警
curl -s "https://crt.sh/?q=yourdomain.com&output=json" | jq '.[] | select(.entry_timestamp > "2026-05-01")'
既检测异常签发、又确认自己的证书在被 log。
怎么确认已经修好
openssl x509 -in cert.pem -noout -text | grep -A 30 "CT Precertificate SCTs"至少返回 2 条 SCT(长有效期证书 3 条)、来自至少 2 家不同的 log。https://crt.sh/?q=yourdomain.com签发后几分钟内能看到新证书序列号。- 原本报错的那台客户端上,Chrome 打开
https://yourdomain.com无错误。 - 在 Chrome 里打开 DevTools → Security 标签(或点地址栏的页面信息按钮看证书),确认证书有效、无 CT 警告。
- 在 Firefox 135+ 和 Safari 上再各试一遍,因为两者现在都强制 CT。只在一个浏览器通过不算数。
- 用干净的 Chrome 无痕窗口、在非企业网上测一次,排除缓存的旧错误或代理专属的失败。
长期预防
- 永远用已知 embed SCT 的 CA:Let’s Encrypt / ZeroSSL / DigiCert / Sectigo / GlobalSign——目前都行。
- 内部 CA 别交叉签到公共 root,除非你打算从公共 root 签发。
- 用
cert-spotter、crt.sh监听、或商业服务(Hardenize / Censys)建 CT 监控,发现 SCT 缺失和异常签发。 - Chrome 大版本发布后做一次校验,自己的证书没有在已取消 log 上。
- 文档化签发流程包括 SCT 送达方式,未知工程师一年后能排查。
常见坑
- 以为”证书链通就有效”。CT 检查独立于链验证。
- 把 Firefox 能开当成证据。Firefox 135+ 现在也强制 CT,旧版 Firefox 能开只说明那份是过期的客户端。
- 原件缺 SCT 时只把同一张证书从 CA 重新下载。你需要的是真正的 reissue(重签),不是重新下载。
- 试图在用户侧 Chrome 关 CT。只对受管设备通过企业策略生效,对普通访客永远无效。
- 忘了 CT 对 ECDSA 和 RSA 同等适用、算法无关。
- 换 CA 或 log 退役后没重新验证 SCT。新路径可能用 TLS 扩展送、被你 server 丢掉,或者依赖了已不再受信任的 log。
FAQ
Q:Firefox 能开、Chrome 拒,那证书到底有没有问题?
不能再这么想了。Firefox 从桌面版 135(2025 年初)和安卓版 145 起强制 CT,Safari / Apple 平台更是强制好多年了。证书过不了 CT,迟早每个主流浏览器都会拒。旧版 Firefox 还能开,说明那份 Firefox 过期了,不代表证书健康。把 Chrome 的错误当成事实,去修证书。
Q:我的证书到底需要几条 SCT?
截至 2026 年 6 月,有效期不超过 180 天的证书至少要 2 条 SCT、有效期超过 180 天的至少 3 条,而且必须来自至少 2 家不同的 log 运营方。现代 CA 会自动处理;你多半只在涉及内部签发流程或过期的 log 列表时才会遇到麻烦。
Q:能让 Chrome 跳过我域名的 CT 检查吗?
只在受管 Chrome 上通过企业策略(CertificateTransparencyEnforcementDisabledForUrls 或 ...DisabledForCas)能做、仅对受管设备生效。对普通公众没有按用户的退出选项,你也不该围着它做设计。
Q:CA 说他们写 CT log、但证书里没 SCT?
很可能 CA log 了证书、但用 TLS 扩展或 OCSP stapling 送 SCT、没 embed,而你的 web server 把它丢了。让 CA 把订单切到 embedded 送达(第 4 步),再重签。Let’s Encrypt / ZeroSSL / DigiCert / Sectigo / GlobalSign 默认就是 embedded。
Q:一张用了好几个月的证书突然报错,为什么?
它依赖的某个 CT log 被取消信任或退役了。在 2026 年从 RFC 6962 log 迁到 static-ct-api log 的过程里这很常见。从你的 CA 重签一张,新证书就会带上来自当前 log 的 SCT。
Q:通配符证书和 ECDSA 证书也走 CT 吗?
一样。通配符跟普通证书走同样的签发流程、需要合格的 SCT;CT 强制也不在乎证书是 RSA 还是 ECDSA。