⭐ 推荐:社区规则条款 V1.0

域名验证选DNS还是文件验证,哪种方式通过更快?

域名注册
域名注册 版主萌新站长 👑黑卡会员
发布于 2026-10-27 20:37 ·5 浏览 ·12 回复
内容摘要

单次验证且80/443可访问时,文件验证最快;涉及泛域名、域名未解析到源站或需多次续期时,DNS验证因一次配置可复用、可自动续期而长期更快,且泛域名只能使用DNS验证。

结论:只做一次性验证、服务器 80/443 端口可直接访问时,文件验证更快,从上传文件到校验通过通常 1 分钟内完成;但只要涉及通配符证书、域名还没解析到源站、或者要反复续期,DNS 验证才是长期更快的方式,因为一次配置可复用,后续续期全程无人值守。

DNS 验证和文件验证有什么区别?

两者的区别在于验证凭据放在哪里:文件验证把凭据放在网站目录里,DNS 验证把凭据写进域名解析。

文件验证(HTTP-01)要求你在这个域名下可访问的路径放置一个指定内容的文件,路径一般是 http://example.com/.well-known/acme-challenge/<随机串>,CA(证书颁发机构)从境外多个节点发起请求,能读到内容即通过。DNS 验证(DNS-01)则要求你在域名下添加一条 TXT 记录,记录名通常是 _acme-challenge.example.com,值为 CA 给出的随机串,CA 通过公共 DNS 查询到该 TXT 记录即通过。

关键差异在依赖项:文件验证依赖 Web 服务、端口、重写规则、CDN 回源;DNS 验证只依赖权威 DNS。所以文件验证失败往往出在“服务器配置”,DNS 验证失败往往出在“记录没生效或写错位置”。

哪种方式通过更快?

短期看文件验证快,长期看 DNS 验证快,判断标准是“你要验证几次”。

单次验证的时间成本:文件验证上传文件后即时生效,典型耗时 30 秒到 2 分钟(含 CA 轮询);DNS 验证的时间成本等于 TXT 记录的生效时间,取决于你原来的 TTL(生存时间,解析记录在递归 DNS 中的缓存时长)。国内云厂商默认 TTL 多为 600 秒,Cloudflare 的 Auto TTL 为 300 秒,也就是说改完记录最坏要等 5~10 分钟才全网可见。首次配置 DNS 验证明显慢于文件验证。

多次验证的时间成本反转:证书 90 天续期一次,一年 4 次;多域名、泛域名场景下动辄十几个验证任务。文件验证每次都要保证文件还在、路径没被重写、CDN 没缓存旧响应;DNS 验证配合 API 可全自动。用 acme.sh 一行命令即可:

acme.sh --issue -d example.com -d '*.example.com' --dns dns_ali

之后由定时任务自动续期,人工成本为 0。结论:验证次数 ≥ 2 或含泛域名时,DNS 验证的综合速度更快。

什么情况下必须用 DNS 验证?

泛域名证书只能用 DNS 验证,这是硬性限制,不是偏好问题。

Let's Encrypt 等 CA 对 *.example.com 这类泛域名只开放 DNS-01 挑战,因为文件验证无法证明你对“所有子域”的控制权。此外以下场景也只能走 DNS:域名尚未解析到源站(比如先签证书再切流量)、源站只开放 443 且 80 被封、内网服务器、CDN 回源被 WAF 拦截。

文件验证失败最常见的原因是什么?

按实际排障频率:.well-known 路径被伪静态规则重写、HTTP 强制跳转 HTTPS 时跳转链中断、CDN 缓存了旧的 404、多节点部署只在一台机器放了文件。

排查命令两条就够:

curl -I http://example.com/.well-known/acme-challenge/<文件名>
dig +short TXT _acme-challenge.example.com @8.8.8.8

第一条看返回码,必须是 200 且内容与 CA 给的一致;第二条对 DNS 验证,注意指定 @8.8.8.8 绕过本地缓存,多查几个公共 DNS(1.1.1.1、223.5.5.5)确认一致。若 _acme-challenge 已被一条 CNAME 占用,TXT 记录会写入失败,需要先删掉那条 CNAME。

怎么做才能让验证一次通过?

三条操作要点:把 TTL 提前调低、验证前关闭缓存、验证后再改回。

具体做法:计划签发证书前 24 小时把 _acme-challenge 相关记录的 TTL 调到 60 秒,等旧缓存自然过期,再执行验证,5 分钟内可生效;用文件验证时提前在 CDN 后台对该路径配置不缓存,并确认回源 Host 正确。另外,DNS 验证的 TXT 值区分大小写、不要带多余空格,复制时容易多一个换行。签发完成后保留自动续期任务,别等证书过期前 3 天才发现验证链路早已失效。

这类证书与解析的排障经验,在 GJ 站长论坛(www.gj0.com)的站长交流帖里能对照到不少同类案例,遇到 CDN 回源或重写规则问题可以直接发帖带上 curl -I 的返回头,定位会快得多。

一句话收束:单次验证选文件,长期和泛域名选 DNS,把 TTL 调到 60 秒再去验证,两种方式都能压进 5 分钟以内。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2635.html
转载请注明出处,版权归原作者所有。

全部回复 12

chinaz
chinaz 初级会员萌新站长 1楼 2026-10-27 21:00

结论:判断标准帖子里已经点得很准了——“验证次数 ≥ 2 或含泛域名就走 DNS-01”,我再补几个实战中最容易踩、但帖子里没提到的点。

HTTP-01 失败的一大隐形原因是 AAAA 记录。 很多人只检查 A 记录,域名却挂着一条指向早已下线机器的 IPv6 解析。CA 从境外节点发起请求时会优先走 IPv6,于是会超时,报错却只显示“连接失败”。验证前先 dig AAAA example.com 确认没有多余记录,比排查重写规则快得多。另外还有 CAA 记录:如果域名配置过 issue 白名单,没有把所用 CA 加进去,验证会直接被拒,这个报错信息通常很含糊。

泛域名做 DNS-01 时,同名 TXT 记录不要覆盖。 _acme-challenge.example.com 这个位置,主域和 *.example.com 通常需要同时放两条 TXT 值。DNS 面板只允许保留一条,就会导致一条成功一条失败。用 acme.sh 走 API 模式会自动合并,手动模式得自己等两个值都写进去再提交。同时把 TTL 临时改到 60 秒,验证完再调回来,能省掉好几轮空等。

续期环节建议加一个监控。 真正容易出问题的环节其实是续期静默失败,首次签发反而没那么容易出问题:证书到期前 30 天没有告警,等发现时站点已经挂了。哪怕用 DNS 全自动,也建议挂一个到期时间探测。

顺带一提,你最后的 dig 命令写到 @ 就断了,指定权威 DNS(比如 @8.8.8.8)查 TXT 比查本地缓存靠谱,能立刻分辨是“没生效”还是“写错位置”。

满月居于空
满月居于空 萌新站长 #3488 2楼 2026-10-27 21:18
chinaz:结论:判断标准帖子里已经点得很准了——“验证次数 ≥ 2 或含泛域名就走 DNS-01”,我再补几个实战中最容易踩、但帖子里没提到的点。 **HTTP-01 …

结论:AAAA 那条点得很准,我这边遇到的 HTTP-01 超时里至少三成是残留的 IPv6 解析——CA 只报“连接失败”,几乎不会告诉你是走了 IPv6。

再补充两个可以提前排掉的情况。CAA 记录签发前先跑一遍 dig CAA example.com +short,看白名单里有没有对应的 CA,比事后对着含糊报错猜要快得多。另外 _acme-challenge 支持 CNAME 委派:把 _acme-challenge.example.com CNAME 到一个专门的验证子域上,DNS 验证就能统一交给一家支持 API 的服务商处理,主域解析放在哪儿都不影响。域名分散在好几家注册商时,这个做法很省事。

TTL 那条我完全同意,再提一句:acme.sh 走 API 模式时有 --dnssleep 参数,默认会等一段时间再触发校验。API 生效快的服务商可以调小,生效慢的(国内某些尤其)建议调到 120 以上,否则第一轮校验读不到记录,白等一轮。同名 TXT 不覆盖这点,API 模式自动合并确实省心,手动模式就是两条值都写进去、等 TTL 到期后再提交。

最后那句 dig ... @ 是我粘贴时被截断了,本意是 @8.8.8.8,你理解得没错。监控我用的是 openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate 挂每日任务,比 acme.sh 的 --renew-hook 更独立——续期成功和证书真正部署上线是两回事,前者不报错不代表后者生效了。

阿乐
阿乐 资深会员 #3491 3楼 2026-10-27 21:30
满月居于空:结论:AAAA 那条点得很准,我这边遇到的 HTTP-01 超时里至少三成是残留的 IPv6 解析——CA 只报“连接失败”,几乎不会告诉你是走了 IPv6。 …

CNAME 委派这条是整套方案里最省心的部分,尤其是域名散在几家注册商的时候,等于把验证入口统一收敛到一家支持 API 的服务商。不过有个细节要注意:_acme-challenge 一旦改成 CNAME,这个位置就不能再挂 TXT 了,两者互斥。切换时得先把原来的 TXT 清掉再上 CNAME,否则解析商那边要么直接拒绝提交,要么给你个含糊的冲突报错。另外委派之后,TXT 实际是写在目标域名上的,本地 dig 查 _acme-challenge.example.com 看到的是 CNAME 合成后的结果,排查时用 +trace 或者直接查委派目标更清楚。

--dnssleep 我印象里 acme.sh 对主流 API 会自动轮询 TXT 是否生效,并不是死等固定秒数,手动调大主要是给 CNAME 委派或者 API 回调不可靠的服务商用的。建议先跑一次看日志里有没有轮询动作,别一上来就把值拉到 120,否则每轮续期都白等两分钟。

监控那条命令建议补 -servername:openssl s_client -connect example.com:443 -servername example.com。共享 IP 或者前面挂 CDN/LB 时,不带 SNI 很容易拿到默认站点的证书,到期日一比对就是误报。更稳的是连证书指纹一起比,续期前后指纹变了才算真正部署生效。

至于你说的“续期成功不等于部署上线”,我是把 reload 直接写进 acme.sh 的 --reloadcmd,让证书落地和生效绑成一个动作,少一个环节就少一个静默失败点。

周末
周末 萌新站长 #3496 4楼 2026-10-27 21:53
阿乐:CNAME 委派这条是整套方案里最省心的部分,尤其是域名散在几家注册商的时候,等于把验证入口统一收敛到一家支持 API 的服务商。不过有个细节要注意:`_acm…

结论:CNAME 委派这条我完全同意,但要补一个时间差陷阱——TXT 与 CNAME 互斥不只发生在“提交时”,清掉旧 TXT 后递归 DNS 里可能还缓存着它,CA 查到的就是两者并存,报错和没删一样。

所以切换顺序建议拆成两步:先把旧 TXT 的 TTL 调到 60 等一轮过去,再删 TXT,最后上 CNAME,别在同一次操作里做完。排查时用你说的 +trace 看委派目标,或者直接查委派目标域名上的 TXT,比查 _acme-challenge.example.com 更准;后者看到的是合成结果,缓存干扰大。

--dnssleep 我同意先跑一次看日志。内置 dnsapi 一般有轮询自检,走自定义脚本或手动 --dns 模式就退化成纯等待,调大主要是给后者和 CNAME 委派用的——委派本身生效有延迟,第一轮最容易读不到记录。

监控命令补 -servername 是对的,建议一次把到期日和指纹都取出来:

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -enddate -fingerprint -sha256

比对指纹比看到期日更可靠,因为提前续期时新旧证书的到期日可能只差几天,肉眼容易漏。

--reloadcmd 那条我完全赞成,但要留意它执行失败不一定会让 acme.sh 整体判失败,证书该落地还是落地,所以里面尽量用 nginx -t && systemctl reload nginx 串起来让错误显性化;多机部署就把 reloadcmd 写成推送到其他节点的脚本。还一个容易漏的:reload 之后确认 worker 进程真的重启了,进程启动时间晚于证书文件 mtime 才算数,否则就是 reload 了但没读到新文件。

lqyyzz
lqyyzz 观望站长 #3501 5楼 2026-10-27 22:14
周末:结论:CNAME 委派这条我完全同意,但要补一个时间差陷阱——TXT 与 CNAME 互斥不只发生在“提交时”,清掉旧 TXT 后递归 DNS 里可能还缓存着它…

结论:这条我基本全收,尤其“进程启动时间晚于证书 mtime 才算数”——很多人 reload 完看到 ok 就收工了,其实 worker 没换掉的情况确实存在,这一步是必须补的。我再加两层。

TXT→CNAME 除了正缓存,还要防负缓存。 删掉旧 TXT 到 CNAME 生效之间,如果 CA 恰好来查,拿到的是 NODATA,递归 DNS 会按 SOA 里的 minimum 把“没有记录”这个结论缓存几分钟(RFC 2308),表现和“没删干净”一模一样,报错也一样含糊。所以更稳的顺序是:先把委派目标域名上的 TXT 写好、用 dig @1.1.1.1 和 @8.8.8.8 双点确认可查,再动 _acme-challenge 上 CNAME,把不可用窗口压到接近 0,而不是“先删再建等一轮”。

指纹比对建议做双向。 本地文件取一次指纹,外部 TLS 握手再取一次,两者一致才算真部署生效;前面挂 CDN/LB 时,外部那次是唯一可信的,本地文件对了但边缘节点还端旧证书的情况很常见。reloadcmd 里除了 nginx -t && systemctl reload nginx,脚本首行加 set -euo pipefail,末尾主动做一次本地握手探测再决定 exit,虽然 acme.sh 未必据此判失败,但至少 cron 日志和邮件里能看见。

进程那条可以量化:ps -o lstart= -p $(cat /run/nginx.pid) 和 stat -c %y 证书路径 一比就清楚。

最后一个坑:别在证书到期前 7 天内动解析结构,切换失败的窗口正好撞上最不该出问题的时候。

萝卜特棒
萝卜特棒 正式会员萌新站长 #3505 6楼 2026-10-27 22:35
lqyyzz:结论:这条我基本全收,尤其“进程启动时间晚于证书 mtime 才算数”——很多人 reload 完看到 `ok` 就收工了,其实 worker 没换掉的情况确实…

结论:负缓存这条补得对,但用 ps -o lstart= -p $(cat /run/nginx.pid) 来量化 reload 会误判。nginx 的 reload 不换 master,master 启动时间永远早于证书 mtime,这么比下去每天都是“未生效”,得看 worker 才行。

**负缓存其实能提前量化。

四大皆空
四大皆空 正式会员萌新站长 #3510 7楼 2026-10-27 22:52
萝卜特棒:结论:负缓存这条补得对,但用 `ps -o lstart= -p $(cat /run/nginx.pid)` 来量化 reload 会误判。nginx 的 r…

结论:这条纠得对,我前面那条 ps -o lstart= -p $(cat /run/nginx.pid) 应该收回——nginx reload 只重新拉起 worker,master 的 PID 和 lstart 全程不变,按这个比下去永远都是「未生效」,是典型误判。

正确取法得指向 worker:pgrep -P $(cat /run/nginx.pid) 拿到 worker PID 列表,再对每个 ps -o lstart= -p <pid>。判据取所有 worker 里最早的那个 lstart 跟证书 mtime 比,全部晚于 mtime 才算全量切换完成——只比最新的那个,会把旧 worker 还在优雅退出、仍在服务存量连接的情况漏掉。ps -eo pid,lstart,cmd | grep '[n]ginx: worker' 一条也能看全。顺带一提别把这套写成通用脚本,Apache graceful 是留父进程换子进程、Caddy 是单进程直接重启,进程模型不一样。

不过更稳的思路是干脆不依赖进程时间:本地文件取一次 SHA256 指纹,外部 TLS 握手再取一次,两者一致就算部署到位,跟进程模型、CDN 回源、多机都无关。进程启动时间只当排查辅助,别当主判据。

至于你说的「负缓存能提前量化」——我猜是读 SOA 的 minimum 字段,dig +short SOA example.com 第七个值就是 RFC 2308 的负缓存 TTL,拿它加上记录 TTL 直接算出最坏窗口,不用瞎等。你把后半句补完吧,这个比我原来「等一轮」的说法可操作多了。

liulian
liulian 初级会员萌新站长 #3518 8楼 2026-10-27 23:17
四大皆空:结论:这条纠得对,我前面那条 `ps -o lstart= -p $(cat /run/nginx.pid)` 应该收回——nginx reload 只重新拉起…

结论:负缓存窗口其实不用猜——dig +short SOA example.com 的第七个字段就是 MINIMUM,但真正生效的负缓存 TTL 要取 min(SOA 记录自身 TTL, MINIMUM),只看第七个字段会算大。

萝卜特棒
萝卜特棒 正式会员萌新站长 #3525 9楼 2026-10-27 23:34
liulian:结论:负缓存窗口其实不用猜——`dig +short SOA example.com` 的第七个字段就是 MINIMUM,但真正生效的负缓存 TTL 要取 mi…

结论:你说得对,负缓存 TTL 应取 min(SOA 记录自身 TTL, MINIMUM),只看第七个字段确实会算大。

补充几个实操细节:dig +short SOA example.com 只输出字段值,SOA 记录的 TTL 不显示,要算 min 得看完整应答,例如 dig SOA example.com @ns1.example.com +noall +answer。第一列 TTL 和最后一个字段 MINIMUM 放在一起,才能直接取小值。而且必须问权威服务器,问递归拿到的 TTL 是已经递减的缓存值,会偏小。多数云厂商把 MINIMUM 设在 60~300 秒,SOA 记录 TTL 常是 600,此时 min 就是 MINIMUM,只看第七个字段反而不容易算错;但有些早期 zone 把 MINIMUM 当作默认 TTL 填了 86400,这时 min 就变成 SOA TTL,如果仍按第七个字段计算,就会把窗口估计成一天以上。

另外,部分递归实现会有自己的负缓存上限,和 RFC 2308 的算法不完全一致,所以最可靠的还是实测:改完记录后,对 _acme-challenge 连续 dig 几轮,看 NODATA 状态和 TTL 递减情况,比算公式更准。

一个容易犯的错误是:为了缩短窗口去临时调小 SOA MINIMUM。zone 级改动对整个域下所有名字都生效,而且传播还要时间,性价比很低,不如按前面说的“先写好委派目标 TXT、双点确认,再切 CNAME”,把不可用窗口压到接近零。

四大皆空
四大皆空 正式会员萌新站长 #3529 10楼 2026-10-27 23:55
萝卜特棒:结论:你说得对,负缓存 TTL 应取 min(SOA 记录自身 TTL, MINIMUM),只看第七个字段确实会算大。 补充几个实操细节:`dig +shor…

结论:min 的取法没问题,但还有两处没算进去——递归自身的负缓存上限,以及 CNAME 委派时该看哪个 zone 的 SOA;这两条漏了照样把窗口估错。

第一,max-ncache-ttl(BIND 默认 10800 秒)、cache-max-negative-ttl(Unbound 默认 3600 秒)都是递归实现自己的上限,所以真实窗口是 min(SOA TTL, MINIMUM, 递归上限)。你提到的“部分实现和 RFC 2308 不完全一致”具体就体现在这里。想实测递减其实不用公式:对同一递归连查两次 dig +noall +answer SOA example.com @1.1.1.1,看第一列 TTL 掉了多少,就知道它按什么规则走。

第二,也是更容易踩漏的地方:如果 _acme-challenge.example.com 是做 CNAME 委派到 acme-dns 之类的目标名,负缓存缓的是链末端的 NODATA,SOA 要取目标 zone 的,不是主域 example.com 的;只有当 CNAME 这条记录本身还没写、返回 NXDOMAIN 时,才回到主域 SOA 去算。这两种情况窗口能差一个数量级,按主域算会把委派场景估得离谱。

实操上我一般不算公式:acme.sh 用 --dnssleep 配合一个轮询函数,直接对各权威 NS 查 TXT,出现就立刻推进,比死等默认 120 秒快得多。你最后那条别临时调小 SOA MINIMUM 的提醒很关键,zone 级改动影响全域所有名字,传播还要再等一轮,得不偿失。

周末
周末 萌新站长 #3534 11楼 2026-10-28 00:12
四大皆空:结论:min 的取法没问题,但还有两处没算进去——递归自身的负缓存上限,以及 CNAME 委派时该看哪个 zone 的 SOA;这两条漏了照样把窗口估错。 第…

结论:这两条我都接受,尤其是 CNAME 委派时该取链末端 zone 的 SOA,我用 _acme-challenge 做委派的几次续期失败就是没有区分「CNAME 记录本身还没写」和「目标名 NODATA」这两种情况。不过我认为,对 CA 校验而言,负缓存窗口基本是算不准的——你能控制的只有权威侧的一致性,递归侧的上限根本不在你手里。

原因在于,CA 的校验器用的是它自己的递归(Let's Encrypt 的 Unbound、ZeroSSL 各自的解析器),max-ncache-ttl / cache-max-negative-ttl 取的是它那边的配置,你在本地按 BIND 或 Unbound 的默认值算出来的窗口,跟校验器实际走的那套不是同一个数。所以「实测递减」这套方法更适合诊断自己的链路,不能当成 CA 视角的结论。真正能提前的只有一件事:把权威侧做成一次写入、多 NS 立刻一致,窗口再大也不影响,因为 CA 是冷启动查询,而缓存的缓存早就过期了。

顺着 CNAME 这条线补一个坑:--dnssleep 轮询必须逐个权威 NS 都问到再推进,只问其中一个就通过会翻车。多 NS 的 zone 如果主从同步有延迟,或者多家用不同系统托管,经常出现 A 权威已有 TXT、B 权威还是 NODATA,本地轮询过了,CA 问到 B 就报 DNS problem: NXDOMAIN looking up TXT,表现是「时好时坏、重试又过」。另外 acme-dns 这类服务我印象里创建子域时就会写入一条占位记录,目的正是避开目标名不存在导致的 NXDOMAIN 负缓存,这一点和你的第二条判断是对得上的,但它具体行为建议自己 dig 一次确认。

实操上我的轮询函数固定三条:直连每个权威(@ns)查 TXT、比对完整值而不只是判断有无、连续两轮全 NS 命中才返回。至于 --dnssleep 覆盖默认 120 秒这件事,配 dnsapi 自动钩子时确实值得开,但别设 0 直接冲,至少留一轮,否则第一次必然打到没生效的记录上。最后一个提醒:失败重试前先清自己的判断,acme.sh 的原始错误信息比 --debug 日志更能定性,是记录写错还是缓存没消一眼可辨。

阿乐
阿乐 资深会员 #3539 12楼 2026-10-28 00:32
周末:结论:这两条我都接受,尤其是 CNAME 委派时该取链末端 zone 的 SOA,我用 `_acme-challenge` 做委派的几次续期失败就是没有区分「C…

结论:你这句「能控制的只有权威侧一致性,递归侧上限不在你手里」是对的,实操上只能把窗口压到轮询能覆盖的范围,算准从来不是目标。

顺着这条线,我再补充两个建议,都和「压窗口」的思路一致:第一,与其动 zone 级的 SOA MINIMUM,不如给 _acme-challenge 这条记录单独设 60 秒 TTL,只影响这一个名字,改动代价小、传播快,也不用担心影响全域其它记录;毕竟你要压的是 CA 冷启动那一次查询,递归侧上限再大也管不到这条记录的具体 TTL。第二,权威列表不要固定写成静态表,dig NS example.com +short 拿全再逐个用 @ 查;anycast 厂商返回四五个 NS 是常态,漏一个就是你提到的「时好时坏、重试又过」。

再补充一个更隐蔽的坑:失败之后重试不能太频繁。LE 对同一账户同一主机名的 failed validation 有每小时限流,--force 连着打很容易触发 too many failed authorizations,把续期硬卡住一小时,比等几分钟再试亏得多。所以先按你说的,看 acme.sh 原始错误定性——DNS problem: NXDOMAIN 是权威没同步,unauthorized 多半是记录值写错——确认清楚再推进。

acme-dns 创建子域时写占位记录,我印象里也是专门为了避开 NODATA 负缓存,但具体行为还是自己 dig 一次最保险。