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 记录类型
| 类型 | 含义 | 例子 |
|---|---|---|
| A | IPv4 地址 | example.com → 1.2.3.4 |
| AAAA | IPv6 地址 | 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 ra | qr=响应、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 调度核心机制:
- 域名 CNAME 到 CDN:
www.example.com CNAME www.example.com.cdn.com - CDN 权威 DNS 智能解析:根据用户 IP(含 ECS)返回最近节点
- 节点 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 里
<link rel="dns-prefetch" href="//api.example.com"> - preconnect:
<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. 延伸阅读
- 《DNS 与 BIND》第 5 版(Cricket Liu) — DNS 权威书
- Cloudflare Learning: What is DNS? — 入门最佳
- RFC 1035 — DNS 协议规范
- RFC 8484 — DoH
- DNSPerf — 实时 DNS 服务商性能排名
- Public DNS resolvers comparison — DoH 服务对比