DNS 污染自测:三条命令,分清污染、劫持、节点故障和网站自己挂了
一个网站打不开,别的都正常
这篇处理的是一种很具体的故障:某一个站点访问不了,而你其他的网站、其他的应用都好好的。
这种情况下,浏览器给你的信息几乎为零——不管是解析被污染、运营商在骗你、代理节点挂了,还是那个网站自己出了问题,浏览器都只会转圈然后报一个含糊的错。
这四种原因的处理方式完全不同,猜错一次就是半小时。 所以这篇不解释原理,只给三条命令和一张判读表。
原理想补的话去这两页:《DNS 污染是什么》 讲投毒机制,《DNS 劫持是怎么回事,怎么防》 讲中间人欺骗。这一页假设你已经知道它们是什么,只想知道你现在遇到的是哪一个。
先说结论
- 三条命令,按顺序跑,不要跳。 第一条判断「有没有人抢答」,第二条判断「正确答案是什么」,第三条判断「拿到正确答案之后还通不通」。
- 第一条是全篇最有价值的一条:向一个按规范不可能有 DNS 服务的地址发查询。收到回答,就证明路上有设备在抢答——不需要任何海外机器配合。
- 不要只看 IP 对不对,要看
dig头部的status:和flags:,以及Query time。一个异常快的回答本身就是信号。 - DoH 是第二条命令的工具,不是结论。它证明「正确答案长什么样」,不证明你连得上。
- 解析对了还是打不开,就别再碰 DNS 设置了,问题在连接层,见《Clash 报错对照表》的连接类一节。
- 开着代理测,结果会不一样,因为客户端接管了 DNS;fake-ip 模式下
ping出198.18.x.x是正常的。先用 DNS 泄露检测 确认现在到底是谁在回答你。
一句话原则:DNS 故障的判断不靠「结果对不对」,靠同一个问题问不同的人,比较他们的答案。三条命令就是三种问法。
准备:确认你手上有什么工具
| 系统 | dig | nslookup | curl |
|---|---|---|---|
| macOS | 自带 | 自带 | 自带 |
| Linux | 多数发行版自带(或装 dnsutils / bind-utils) | 自带 | 自带 |
| Windows | 不自带,需安装 BIND 工具包 | 自带 | Windows 10 1803 起自带 |
Windows 用户如果不想装 BIND,第一条命令用 nslookup 也能做,写法在下面一并给出。
先做一件事:把浏览器关了。 浏览器有自己的 DNS 缓存和自己的 DoH 设置,它的行为和命令行不一定一致,会干扰你的判断。
第一条命令:有没有人在抢答
原理
DNS 的明文查询走 UDP,没有身份验证——谁先回答,客户端就信谁。所以路上的设备只要抢在真正的解析服务器之前回一个包,就能决定你去哪里。
利用这一点可以做一个非常干净的测试:向一个不可能运行 DNS 服务的地址提问。 如果还有人回答,那个回答必然是伪造的。
选哪个地址?用 RFC 5737 规定的文档专用段。规范原文:
The blocks 192.0.2.0/24 (TEST-NET-1), 198.51.100.0/24 (TEST-NET-2), and 203.0.113.0/24 (TEST-NET-3) are provided for use in documentation.
按定义,这些地址只用于文档示例,不会有任何真实服务跑在上面。
命令
dig +timeout=3 +tries=1 @192.0.2.1 你要测的域名
参数依据 ISC 的 BIND 官方手册:@server 指定要查询的域名服务器;+timeout=T 是「sets the timeout for a query to T seconds」(把一次查询的超时设为 T 秒,默认 5 秒);+tries 设置重试次数。设成 3 秒 1 次,是为了让「超时」这个结果快点出来。
你可能在别处见到写成 +time=3 的版本,两种写法都对。 手册在「Query Options」一节写明「Keywords may be abbreviated, provided the abbreviation is unambiguous」(关键字可以缩写,只要缩写不产生歧义),+time 就是 +timeout 的一个不产生歧义的缩写;部分随系统附带的旧版 dig(例如 macOS 自带的 BIND 9.10)在自己的 dig -h 里直接把这个选项印成 +time=###。照手册写全名最保险。
Windows 用 nslookup 的等价写法:
nslookup 你要测的域名 192.0.2.1
怎么读结果
| 你看到的 | 结论 |
|---|---|
;; connection timed out; no servers could be reached | 正常。没人抢答,这一层是干净的 |
几秒内返回了一个 ANSWER SECTION,里面有 IP | 有人在抢答。这个答案不可能来自 192.0.2.1 |
nslookup 下:返回了地址而不是 timed out | 同上 |
关于那条「正常」结果:connection timed out; no servers could be reached 是这条命令符合规范时应有的输出——RFC 5737 把 192.0.2.0/24 留给文档使用,那里不该有任何东西回应你,所以「没人应答」正是对的,不是命令写错了。
但请不要直接相信这句话,先给自己立一个基准。 拿一个你确信没问题的网络(例如一台境外服务器,或手机热点换一条链路)跑一次这条命令:
- 看到超时 → 说明命令写对了、这个测试在你手上是有效的,这就是你的基准;
- 在这条「干净」链路上也立刻拿到了 IP → 先别下污染的结论,多半是你把地址或参数敲错了,或者这台机器上有本地解析软件在代答,把命令逐字核一遍再说。
有了基准,再回到出故障的那个网络跑第二次。两次结果不同,才是证据;只跑一次拿到的任何结果,都还不能区分「链路有问题」和「这条命令本来就跑不对」。
如果这一条就抓到了抢答,那你遇到的是路上的问题,换一个公共 DNS 地址没有用——因为抢答者不在乎你问的是谁。这种情况直接跳到第二条命令,用 DoH 拿正确答案。
顺手多看两眼头部
即使没抓到抢答,dig 的头部也值得读。一次正常查询的输出格式是这样(下面是 dig 的标准输出样式,用来说明该看哪几行;具体的 id、IP、耗时每次都不一样):
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24104
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
example.com. 166 IN A 172.66.147.243
;; Query time: 28 msec
;; SERVER: 1.1.1.1#53(1.1.1.1)
要看的四处:
status:——NOERROR是成功;NXDOMAIN表示这个域名不存在(这是域名本身的状态,不是网络问题);SERVFAIL表示服务器处理失败。flags:——ra表示对方支持递归。一个没有ra却给了你答案的响应值得怀疑。Query time—— 和你平时查同一个 DNS 的耗时比。明显更快的回答往往来自更近的地方,而更近的地方不一定是你想问的那个人。SERVER:—— 确认它真的问了你指定的那台,而不是回落到了系统默认。
第二条命令:正确答案到底是什么
原理
第一条只告诉你「有没有被动手脚」,不告诉你正确答案。要拿正确答案,得让查询在路上不可读——这就是 DoH(DNS over HTTPS)的作用:查询被包在 HTTPS 里,中间设备看不见内容,也就无从抢答。
命令
三家提供商,地址都取自它们自己的文档:
Cloudflare(官方文档给出的调用形式,原样照抄):
curl --header "accept: application/dns-json" \
"https://cloudflare-dns.com/dns-query?name=你要测的域名&type=A"
Google(官方文档写明 Google Public DNS 提供两套 DoH 接口:dns.google/dns-query 走 RFC 8484,dns.google/resolve 是 JSON 接口、仅支持 GET):
curl "https://dns.google/resolve?name=你要测的域名&type=A"
阿里云公共 DNS(官网列出的 DoH/DoT 地址是 dns.alidns.com,IPv4 为 223.5.5.5、223.6.6.6):
curl "https://dns.alidns.com/resolve?name=你要测的域名&type=A"
为什么要同时跑境内和境外两家:它们的答案不一致,本身就是有用的信息——很多国际站点在国内有独立的 CDN 节点,答案不同不等于有人作恶。境内外都问一遍,才分得清「被污染」和「本来就该给你不同的 IP」。
不想敲命令的话,本站的 DNS 记录查询 就是基于 DoH 的,可以直接在浏览器里做这一步。
怎么读结果
返回的 JSON 里,Status 字段就是 DNS 响应码:0 是 NOERROR(成功),3 是 NXDOMAIN(域名不存在)。Answer 数组里的 data 就是解析出的地址,TTL 是它的剩余缓存时间。
| 对比结果 | 结论 |
|---|---|
DoH 有答案,明文 dig 的答案不同 | 明文那条被动过。用 DoH 的答案继续第三条 |
| DoH 和明文答案一致 | DNS 这一层没问题,直接做第三条 |
DoH 的 Status 是 3(NXDOMAIN) | 这个域名确实不存在——拼错了,或者它真的没了 |
| 境内外 DoH 答案不同,但两个都连得通 | 正常的 CDN 就近解析,不是故障 |
| DoH 请求本身连不上 | 你到那个 DoH 服务的这条路不通,换一家再试 |
DoH 不是万能的,这一点《DoH 与 ECH》那一页说得更完整。 它只加密了「问」的这一段。拿到正确 IP 之后能不能连上,是完全独立的另一件事——也就是第三条命令。
第三条命令:拿到正确答案之后,还通不通
原理
前两条都成功,你现在手上有一个可信的 IP。但「知道地址」和「能连上」是两回事。第三条命令的作用是绕开本机的一切 DNS 解析,直接拿这个 IP 去建立连接。
curl 的 --resolve 参数正是为此设计的:它把「域名 + 端口」硬绑到你指定的 IP,跳过解析步骤,同时保留域名用于 TLS 的 SNI 和 HTTP 的 Host 头——这一点很关键,因为直接 curl https://那个IP/ 会因为证书不匹配而失败,那属于你自己制造的错误。
命令
curl -v --max-time 10 \
--resolve 你要测的域名:443:第二条命令拿到的IP \
https://你要测的域名/
怎么读结果
| 你看到的 | 结论 | 下一步 |
|---|---|---|
| TLS 握手成功,拿到 HTTP 响应 | DNS 是对的,连接也是通的 | 问题在你本机:DNS 缓存旧了,或 hosts 文件里有条目 |
| 连上了但在 TLS 握手阶段被断开 | 解析没问题,连接层被打断 | 不要再改 DNS。见《Clash 报错对照表》 |
| 连接超时,压根没握上手 | 到这个 IP 的路不通 | 可能是节点问题,也可能是这个 IP 本身不可达 |
| 证书错误,提示域名不匹配 | 你连到的那台机器不是这个站点 | 回到第二条,换一家 DoH 再取一次 |
第一行那个结论最常被忽略:如果直连 IP 一切正常,浏览器却打不开,那就是本机缓存问题,清一次 DNS 缓存就好——改 DNS 服务器、换节点、重装客户端全都是白费力气。
判读表:三条结果合起来看
把三条命令的结果按下表对号入座:
| ① 抢答测试 | ② DoH 答案 | ③ 直连那个 IP | 你的病是 | 按什么顺序处理 |
|---|---|---|---|---|
| 超时(正常) | 与明文一致 | 通 | 本机缓存或 hosts | 清 DNS 缓存 → 检查 hosts → 重启浏览器 |
| 超时(正常) | 与明文一致 | 不通 | 连接层问题,与 DNS 无关 | 换节点 → 查客户端日志 → 别碰 DNS 设置 |
| 超时(正常) | 与明文不一致 | — | 你问的那台解析服务器在给你别的答案 | 换公共 DNS → 改用 DoH → 复测 |
| 有人抢答 | 与明文不一致 | — | 路上有设备抢答 | 换 DNS 没用;改用 DoH/加密通道 |
| 超时(正常) | Status: 3 | — | 域名不存在 | 检查拼写;确认这个站还在不在 |
| 超时(正常) | 取不到 | — | 到 DoH 服务的路也不通 | 换一家 DoH;这往往说明问题更靠上游 |
第三行和第四行的区别,就是「劫持」和「污染」的区别,也是这张表最值得记住的一格:
- 换一个 DNS 服务器就好了 → 问题在你原来问的那一方。
- 换谁问都一样,连不存在的地址都会抢答 → 问题在路上,换谁问都没用。
开着代理的时候,测的是谁
这是最容易得出错误结论的场景。代理客户端会接管 DNS,所以你在本机跑的明文 dig,问到的很可能是客户端的内部 DNS 模块,而不是外面的任何一台服务器。
两个必须知道的现象:
- fake-ip 模式下,
ping域名得到198.18.x.x是正常的。 mihomo 官方文档的示例里fake-ip-range默认就是198.18.0.1/16——它给每个域名发一个假的内网地址,真正的解析推迟到连接建立时才做。这不是劫持。 - 客户端的 DNS 配置本身可能就是故障源。 订阅提供的配置文件包含完整的 DNS 设置和分流规则,这些全部由配置提供方决定。
所以在代理开启的情况下,正确的第一步不是跑 dig,而是先搞清楚现在是谁在回答你:
- 用 DNS 泄露检测 看最终出去的解析服务器落在哪里;
- 如果结果里出现了你本地运营商的 DNS,那是泄漏,原理和修法见《DNS 泄漏是什么,怎么修》;
- 确认了是谁在回答之后,再回到本文第一条命令。
最后:这三条命令查不到的东西
诚实地划一下边界。下面这些情况,三条命令都会显示「一切正常」:
- 网站自己挂了,或者对你所在的地区做了限制——解析正常、连接正常,只是内容不给你。
- 你的账号被限制了,和网络无关。
- 证书链、时间不同步这类本机问题——表现是握手失败,但根因不在 DNS。
- 只有某个 App 不行,浏览器正常——那多半是那个程序没走代理,见《代理开着却还是超时》。
三条命令的价值不是解决问题,是把问题缩小到一层。 缩小之后,剩下的排查才有方向——否则你会在 DNS 设置里改上半小时,而故障压根不在那里。
参考资料
以下文档均于 2026 年 9 月 20—21 日核对;三个 DoH 接口的调用形式(端点、请求头、返回字段)均按各家自己文档的写法照录,你可以在自己机器上用上面的命令逐条复现。
命令与参数
- ISC BIND 9 官方手册(
dig查询选项) —@server、+timeout=T(「sets the timeout for a query to T seconds」,默认 5 秒)、+tries、+short、+tcp、+trace、+recurse/+norecurse的定义原文,以及「Keywords may be abbreviated, provided the abbreviation is unambiguous」这一条(+time能用的依据) - Microsoft —
nslookup命令参考 — Windows 自带工具的用法
地址与规范
- RFC 5737 — IPv4 Address Blocks Reserved for Documentation — 192.0.2.0/24(TEST-NET-1)等三个文档专用段的定义原文
DoH 服务(均取自提供商自己的文档)
- Cloudflare — DNS over HTTPS 的 JSON 接口 — 端点
cloudflare-dns.com/dns-query、必需的accept: application/dns-json请求头、Status字段含义 - Google Public DNS — DNS over HTTPS — 两套接口
dns.google/dns-query(RFC 8484)与dns.google/resolve(JSON,仅 GET) - 阿里云公共 DNS 官网 — IPv4 地址 223.5.5.5 / 223.6.6.6,DoH/DoT 地址
dns.alidns.com
代理客户端侧
- mihomo 官方文档 — DNS 配置 —
enhanced-mode: fake-ip与示例中fake-ip-range: 198.18.0.1/16
将本指南加入收藏夹
跨境网络环境瞬息万变。建议按下 Ctrl+D (Windows) 或 Cmd+D (Mac) 收藏本页,以便在连接波动时快速查阅解决方案。
加入 5,000+ 跨境从业者,第一时间获取最新的 GFW 封锁动态与协议升级提醒。
* 我们绝不发送垃圾邮件,您可以随时取消订阅。
KUAJIE VPN