技术指南

DNS 解析故障:从权威服务器查到本机缓存

用 dig 分层定位委派、权威数据、递归缓存、DNSSEC、本机解析与 Cloudflare 代理问题。

DNS 故障常被笼统地归因于“传播慢”,但一次查询实际经过注册局委派、权威服务器、递归解析器和本机解析栈,浏览器还可能使用独立的加密 DNS。先固定域名、记录类型、查询时间和观察位置,再逐层对比;不要一开始就清缓存或改多处配置。

先固定一次故障样本

字段需要记录的值为什么重要
查询对象完整域名与 A、AAAA、CNAME 等类型不同类型可以同时呈现不同状态
观察位置网络、设备、递归解析器地址办公室、移动网络和浏览器可能走不同路径
时间与期望UTC 时间、预期值、实际值TTL 和缓存剩余时间会持续变化
最近变更委派、记录、DNSSEC、代理或证书把排查范围收敛到真正发生变化的层

先准备变量并确认工具版本。`ZONE` 是权威区域,`NAME` 是发生问题的完整名称,两者不一定相同。后续命令默认使用 BIND 的 `dig`;输出中的 `status`、flags、ANSWER、AUTHORITY 和剩余 TTL 都应保留。

替换为获授权域名;不要只保存 +short 的值
ZONE=example.com
NAME=www.example.com
TYPE=A

dig -v
dig "$NAME" "$TYPE" +noall +answer +authority +comments

先按响应类别分流

响应含义下一步
NOERROR 且有答案查询成功;值仍可能不是预期比较权威与多个递归解析器
NOERROR 但无目标答案通常是 NODATA:名称存在,但该类型不存在查看 AUTHORITY 中的 SOA 和其他记录类型
NXDOMAIN解析器判断名称不存在核对委派、拼写和负缓存 TTL
SERVFAIL解析过程失败,常见于 DNSSEC 或上游不可达比较验证与关闭验证的诊断结果
超时或 REFUSED网络、ACL、服务或查询策略阻止响应检查 UDP/TCP 53、服务状态与访问规则

从父区委派走到权威服务器

`+trace` 从根开始迭代查询,适合确认父区给出的 NS 和 glue 是否能一路到达当前权威服务器。它不是普通用户请求的完整复现,因为它绕过了日常递归缓存;它的作用是定位委派链,而不是证明所有解析器都已更新。

确认 AUTH_NS 非空,并检查权威响应是否带 aa flag
dig +trace "$ZONE" NS

AUTH_NS=$(dig +short NS "$ZONE" | sort | sed -n '1p')
printf 'authoritative server: %s\n' "$AUTH_NS"
dig @"$AUTH_NS" "$NAME" "$TYPE" +norecurse +noall +answer +authority +comments

若父区仍委派给旧服务器,应在注册商处修正 nameserver;若委派正确但权威答案错误,应在实际权威 DNS 提供商处改记录。只改另一套未被委派的控制台不会影响公网结果。多台权威服务器答案不一致时,应先停止继续发布并检查区域同步。

比较递归缓存,不把它叫作玄学传播

比较状态、答案和剩余 TTL;TTL 不同通常只是缓存进入时间不同
for RESOLVER in 1.1.1.1 8.8.8.8; do
  printf '\n== %s ==\n' "$RESOLVER"
  dig @"$RESOLVER" "$NAME" "$TYPE" +noall +answer +authority +comments
done

权威答案已经正确而递归结果仍旧,通常应等待旧 TTL 到期,并用变更前记录的 TTL 估算窗口。不要反复降低 TTL 来修正已经缓存的旧值;降低 TTL 只影响随后取得的新答案。对一个确认不存在且没有 wildcard 命中的测试名称查询,可观察 NXDOMAIN 或 NODATA 以及 AUTHORITY 中用于负缓存的 SOA。

仅使用自己控制、确认不存在且不包含敏感信息的名称
MISSING=known-absent.example.com
dig @1.1.1.1 "$MISSING" A +noall +answer +authority +comments

把 CNAME、IPv4 和 IPv6 分开核对

不要只查 A 记录就断定整个名称正常
for QUERY_TYPE in CNAME A AAAA; do
  printf '\n== %s ==\n' "$QUERY_TYPE"
  dig "$NAME" "$QUERY_TYPE" +noall +answer +authority +comments
done

RFC 1034 要求存在 CNAME 的节点不能再承载其他数据。若同名 A、AAAA 或 CNAME 冲突,应回到权威配置修正;区域 apex 通常还必须承载 SOA 和 NS,因此普通 CNAME 不适用,供应商的 flattening 或 ALIAS 属于各自实现。AAAA 存在只表示发布了 IPv6 地址,不证明客户端有可用 IPv6 路由。

Cloudflare 代理下不要拿源站地址作预期

Cloudflare 官方说明,被代理的 A、AAAA 或 CNAME 查询会返回 Cloudflare anycast 地址而不是源站地址;这是预期行为。不要为了“看见真实 IP”随手切到 DNS only:这样会公开源站,并移除代理层的缓存、防护和证书终止。先在控制台核对代理状态,再从 HTTP 层验证。

替换为受控源站地址;命令保留 Host 与 TLS 名称,不改公共 DNS
ORIGIN_IP=192.0.2.10
curl --resolve "${NAME}:443:${ORIGIN_IP}" \
  --connect-timeout 5 --head "https://${NAME}/"

用验证差异定位 DNSSEC

保留 status、AD flag、RRSIG、DS 与 Extended DNS Error
dig @1.1.1.1 "$NAME" "$TYPE" +dnssec
dig @1.1.1.1 "$NAME" "$TYPE" +dnssec +cdflag
dig +trace "$ZONE" DS

若正常验证返回 SERVFAIL,而 `+cdflag` 能取得未验证答案,DNSSEC 链很可能存在问题。`+cdflag` 只是诊断手段,不是修复,也不应让应用长期绕过验证。核对注册商父区中的 DS 与当前权威提供商发布的 DNSKEY;迁移 DNS 时尤其要按提供商顺序更新或移除旧 DS。

最后检查本机解析路径

getent 更接近应用使用的系统解析路径;resolvectl 只适用于启用 systemd-resolved 的主机
getent ahosts "$NAME"

if systemctl is-active --quiet systemd-resolved; then
  resolvectl status
  resolvectl query --type="$TYPE" "$NAME"
else
  printf '%s\n' 'systemd-resolved is not active; inspect the resolver used by this host'
fi

只有在确认 `systemd-resolved` 正在使用且需要重试本机缓存时,才执行 `sudo resolvectl flush-caches`。它不会清除公共递归解析器或浏览器自己的缓存。浏览器的 Secure DNS / DoH 还可能绕过操作系统配置,因此应分别记录浏览器和命令行结果。

解析正确后转向传输与应用

分别验证 IPv4 与 IPv6;失败时记录具体连接或 TLS 错误
curl -4 --head --connect-timeout 5 "https://${NAME}/"
curl -6 --head --connect-timeout 5 "https://${NAME}/"
证据组合更可能的故障层
权威错误,递归一致错误权威记录或区域发布
权威正确,部分递归仍旧正缓存或负缓存尚未到期
验证 SERVFAIL,+cdflag 有答案DNSSEC DS/DNSKEY/签名链
dig 正确,getent 或浏览器不同本机解析、VPN、DoH 或 hosts
A/AAAA 正确,curl 连接或 TLS 失败路由、防火墙、证书、代理或应用

用可复现结果结束排查

  • 父区委派和每台权威服务器对目标类型返回一致结果。
  • 至少两个递归解析器在预期 TTL 窗口后返回正确状态和答案。
  • 启用验证的解析器不再 SERVFAIL,DS 与 DNSKEY 属于当前提供商。
  • 系统解析、浏览器解析、IPv4、IPv6 和 Cloudflare 代理路径分别有明确结论。
  • 变更项、旧值、新值、时间、回滚方法和原始命令输出进入事件记录。

返回知识库