跳到主要内容

DNS解析全链路

1. 概念与原理

DNS 是互联网的电话簿,把域名翻译成 IP。前端关心 DNS 的真正原因:

  • 首屏性能:DNS 是 HTTP 请求第一步,慢 = 全慢
  • 灰度发布、A/B 测试:智能 DNS 按用户路由
  • 故障切换:DNS 改 IP 让流量切走
  • 域名劫持、DNS 污染:用户访问到错误站点

1.1 域名层级与权威

www.example.com.
└┬┘ └─┬───┘ └┬┘ └ 根(点,平时省略)
│ │ └ 顶级域 TLD(com、org、cn)
│ └ 二级域(example)
└ 主机名(www)

每一级有自己的权威 DNS 服务器(NS):

  • 根服务器(13 组,A-M)管 TLD
  • TLD 服务器管二级域
  • 二级域服务器管你的所有子域

1.2 递归 vs 迭代

用户 → 本地 DNS(递归解析器,如 8.8.8.8)

│ 迭代查询

根 → "去问 .com 服务器"
.com → "去问 example.com 的 NS"
example.com NS → "www.example.com 是 1.2.3.4"

本地 DNS ←────┘
缓存 TTL 秒

返回用户
  • 递归:用户发一次,本地 DNS 负责全部找到答案返回。多数客户端用递归
  • 迭代:服务器只返回"下一步问谁",需要客户端继续查。本地 DNS 对上游用迭代

1.3 记录类型

类型含义例子
AIPv4 地址example.com → 1.2.3.4
AAAAIPv6 地址example.com → 2606:2800::1
CNAME别名(指向另一个域名)www.example.com → example.com
MX邮件服务器example.com → mail.example.com pri 10
TXT任意文本SPF、域名所有权验证、DKIM
NS域名服务器example.com → ns1.dnspod.net
SOA起始授权区域权威信息
SRV服务记录_sip._tcp.example.com → 5060 sip.example.com
CAA证书颁发授权限制谁能签发该域名证书
ALIAS / ANAME根域 CNAME(厂商专有)example.com → cdn.cloudflare.net

CNAME 限制:根域(example.com)不能用 CNAME(与 NS、SOA 冲突),子域可以。需要根域指向 CDN 用 ALIAS / ANAME(厂商扩展)或 CNAME flattening。

2. 解析全流程

2.1 浏览器到 DNS 服务器的路径

1. 浏览器 DNS 缓存(约 60s)
2. OS DNS 缓存(macOS mDNSResponder、Windows DNS Client、Linux nscd/systemd-resolved)
3. /etc/hosts(本地静态映射)
4. 本地 DNS 服务器(路由器 / ISP / 自配)
├── 缓存命中:直接返回
└── 缓存未命中:迭代查询
├── 根服务器
├── TLD 服务器
└── 权威服务器
5. 返回结果,逐级缓存

前端排查 DNS 不一致:清缓存的顺序

# 浏览器:chrome://net-internals/#dns
# macOS
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

# Linux systemd
sudo systemd-resolve --flush-caches
sudo resolvectl flush-caches

# Windows
ipconfig /flushdns

# 本地 DNS(你的路由器或 ISP)通常没法清,只能等 TTL

2.2 TTL 与缓存策略

每条记录有 TTL(秒),决定缓存时长。

  • TTL 太大:改 IP 后流量切换慢
  • TTL 太小:DNS 查询压力大、解析延迟

经验值:

场景TTL
稳定生产3600(1 小时)
准备切换前60(提前调小)
灰度/分流60
极致首屏86400(1 天,配合 CDN 用)

2.3 DNS over UDP 与 TCP

  • UDP 53:默认,包小(< 512 字节)
  • TCP 53:响应超 512 字节自动切换(DNSSEC、大量记录)
  • DoT(DNS over TLS):853 端口,加密
  • DoH(DNS over HTTPS):443 端口,绕过封锁(浏览器主推)

DoH 是隐私和反劫持的方向。Chrome、Firefox 默认尝试启用:

  • Chrome:chrome://flags/#dns-over-https
  • Cloudflare DoH:https://cloudflare-dns.com/dns-query

3. dig 实战详解

3.1 完整输出解读

$ dig example.com

; <<>> DiG 9.10.6 <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; QUESTION SECTION:
;example.com. IN A

;; ANSWER SECTION:
example.com. 300 IN A 93.184.216.34

;; Query time: 12 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Thu Jun 18 10:00:00 CST 2026
;; MSG SIZE rcvd: 56
字段含义
status: NOERROR成功;NXDOMAIN = 不存在;SERVFAIL = 服务器错
flags: qr rd raqr=响应、rd=客户端要求递归、ra=服务器支持递归
TTL 300缓存 5 分钟
Query time解析耗时

3.2 调试场景

# 跟踪完整迭代过程
dig +trace example.com

# 看 NS(找权威)
dig example.com NS +short

# 查权威是否同步
for ns in $(dig example.com NS +short); do
echo "=== $ns ==="
dig @$ns example.com A +short
done

# 不同公共 DNS 看结果是否一致
for dns in 8.8.8.8 1.1.1.1 223.5.5.5 114.114.114.114; do
echo "$dns: $(dig @$dns example.com +short | head -1)"
done

# 反向解析(IP → 域名)
dig -x 1.2.3.4
# 或
host 1.2.3.4

# 看 CNAME 链
dig www.example.com +trace

3.3 EDNS Client Subnet(ECS)

智能 DNS 按用户 IP 段返回不同 IP(CDN 调度核心)。EDNS 让递归 DNS 把用户子网告诉权威服务器。

# 携带 ECS 查询
dig @8.8.8.8 example.com +subnet=1.2.3.0/24

前端实战:CDN 调度异常时,用 dig +subnet 模拟不同地区用户看分配到哪个节点。

4. /etc/hosts 与本地解析

/etc/hosts 是最高优先级(在 DNS 之前),常用于:

  • 本地开发:把域名指向 127.0.0.1
  • 灰度测试:把生产域名指向预发 IP
  • 屏蔽广告:把广告域名指向 0.0.0.0
# /etc/hosts
127.0.0.1 api.local.example.com
1.2.3.4 staging.example.com

修改后无需重启网络。注意有些应用有自己的 DNS 解析(不走 OS),如 Node 的 dns.lookup 走,但 HTTP 库可能用 dns.resolve 不走 hosts。

Node.js DNS 注意

const dns = require('dns')

// 走 OS 解析(含 hosts)
dns.lookup('example.com', (err, addr) => {})

// 不走 hosts,直接 DNS 查询
dns.resolve('example.com', 'A', (err, addrs) => {})

// HTTP 客户端默认用 lookup,所以 hosts 生效

但 Node 18+ 默认偏好 IPv6(verbatim: true),有时会拿到 AAAA 优先返回 IPv6,本地网络不支持 IPv6 导致连接超时。解决:

dns.setDefaultResultOrder('ipv4first')
// 或环境变量 NODE_OPTIONS=--dns-result-order=ipv4first

5. 配置 DNS 服务器

5.1 修改本机 DNS

macOS:系统设置 → 网络 → 配置 DNS。

Linux systemd-resolved

# /etc/systemd/resolved.conf
[Resolve]
DNS=1.1.1.1 8.8.8.8
FallbackDNS=223.5.5.5
DNSStubListener=yes

systemctl restart systemd-resolved
resolvectl status # 看当前生效配置

老式 /etc/resolv.conf(被 NetworkManager / systemd-resolved 接管,直接改可能被覆盖):

nameserver 1.1.1.1
nameserver 8.8.8.8
options timeout:1 attempts:2 rotate

5.2 自建 DNS(dnsmasq 简易方案)

开发环境想给一堆 *.local.dev 都解析到本机:

# 安装
brew install dnsmasq # macOS
apt install dnsmasq # Ubuntu

# 配置 /usr/local/etc/dnsmasq.conf
address=/local.dev/127.0.0.1
listen-address=127.0.0.1

# 启动
sudo brew services start dnsmasq

# 把 .local.dev 域指向 dnsmasq(macOS)
sudo mkdir -p /etc/resolver
echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/local.dev

测试:dig anything.local.dev 应返回 127.0.0.1。

6. CDN 与智能 DNS

CDN 调度核心机制:

  1. 域名 CNAME 到 CDNwww.example.com CNAME www.example.com.cdn.com
  2. CDN 权威 DNS 智能解析:根据用户 IP(含 ECS)返回最近节点
  3. 节点 IP 即用户访问目标
用户 → 本地 DNS
→ 问 example.com 的 NS(域名注册商)
→ 返回 CNAME: www.cdn.com
→ 问 cdn.com 的 NS(CDN 厂商权威)
→ CDN 智能 DNS 看用户 IP → 北京用户 返回北京节点 IP
→ 用户连北京节点

前端排查 CDN 命中

# 看解析到哪个 IP
dig www.example.com +short

# 看完整 CNAME 链
dig www.example.com +trace

# 模拟其他地区用户
dig @<其他地区 DNS> www.example.com

# 看响应头(CDN 一般有标识)
curl -I https://www.example.com
# X-Cache: HIT
# X-Served-By: cache-bj-001

7. 性能优化

7.1 减少 DNS 查询

  • 域名合并:少用 cdn1.x.com / cdn2.x.com / api.x.com 三个域名拆静态资源(HTTP/2 时代弊大于利,看模块 09)
  • DNS prefetch:HTML 里 &lt;link rel="dns-prefetch" href="//api.example.com">
  • preconnect&lt;link rel="preconnect" href="https://api.example.com">(含 DNS + TCP + TLS)
  • 浏览器内置缓存:60s 不可控

7.2 DNS 服务器选择

国内:

  • 223.5.5.5 / 223.6.6.6(阿里 DNS)
  • 119.29.29.29(DNSPod,腾讯)
  • 114.114.114.114(114DNS)

国际:

  • 1.1.1.1(Cloudflare,最快,主打隐私)
  • 8.8.8.8(Google,最稳)
  • 9.9.9.9(Quad9,安全过滤)

测试本机最快:

for dns in 8.8.8.8 1.1.1.1 223.5.5.5 114.114.114.114; do
echo -n "$dns: "
dig @$dns example.com +stats +noall | grep "Query time"
done

8. 故障排查

8.1 NXDOMAIN(域名不存在)

dig example.com +short
# 空
dig example.com | grep status
# status: NXDOMAIN

原因:

  • 域名真不存在 / 拼写错
  • 域名过期
  • 权威 NS 配置错误
  • DNS 劫持(恶意运营商)

排查:

# 1. 查 NS 是否正常
dig example.com NS

# 2. 直接问权威
dig @<ns-ip> example.com

# 3. 看 whois 域名注册状态
whois example.com | grep -i "status\|expire"

8.2 SERVFAIL(服务器故障)

通常是权威 NS 故障或 DNSSEC 验证失败。

# 跳过 DNSSEC 验证
dig +cd example.com
# 如果返回正常,是 DNSSEC 问题

8.3 解析慢

# 1. 测各级耗时
dig example.com +stats | grep "Query time"

# 2. 跟踪迭代看哪一级慢
dig +trace example.com

# 3. 换 DNS 服务器对比

DNS 解析慢通常 1-2 秒就算异常。> 5 秒大概率是上游 DNS 故障。

8.4 浏览器一直 pending DNS

老 macOS Big Sur 有 mDNSResponder bug,缓存损坏后所有 DNS 卡死。sudo killall -HUP mDNSResponder 救命。

9. DNS 安全

9.1 DNSSEC

DNS 应答签名验证,防伪造。配置在权威端,递归端验证。前端默认开启。

9.2 防域名劫持

  • 用 DoH/DoT
  • HTTPS(即使被劫持到错 IP,TLS 证书不匹配)
  • HPKP / Certificate Transparency

9.3 防 DNS 缓存投毒

  • 用支持 DNS Cookie / 0x20 mixed case 的递归 DNS
  • DNSSEC

9.4 域名安全

  • 注册商账号开 2FA
  • DNS 服务商账号开 2FA
  • 锁定关键记录(registrar lock)
  • 配置 CAA 记录限制谁能签发证书:
example.com. CAA 0 issue "letsencrypt.org"
example.com. CAA 0 iodef "mailto:security@example.com"

10. 常见反模式

  • TTL 一直设 86400:故障切换要等一天才生效。重要域名 TTL 不宜超 1 小时
  • 根域用 CNAME:违反 RFC,部分 resolver 拒绝。用 ALIAS 或 A 记录
  • CNAME 链 > 5 跳:每跳一次查询,性能差,部分 resolver 截断
  • /etc/hosts 写完不清缓存:浏览器 / mDNSResponder 还缓存老结果
  • 改 NS 不调 TTL:旧 NS TTL 86400 = 24 小时内可能还指向旧 NS
  • 不监控权威 NS:NS 挂了你不知道,等用户报障
  • DNS 查询走 ISP DNS:可能被劫持插广告。换 1.1.1.1 / 阿里
  • 生产前不预热:切换 IP 前一周把 TTL 调到 60
  • Node 应用没设 --dns-result-order=ipv4first:本地 IPv6 不通时所有请求超时

11. 延伸阅读