中文 English

我的博客怎么跳到百度了?一次 DNS 污染排查,以及 AdGuard Home 真正该打开的开关

发布时间: 2026-09-17 · 阅读量 --
DNS 域名解析 网络 Network 故障排查 Troubleshooting 安全 Security 自动化 Automation

先说结论:这次确实是原有 DNS 解析链路出了问题。 HTTP 请求被送到错误地址,再收到跳往百度移动版的 302;同一个域名指定正确的站点地址后,HTTPS 证书验证通过,网页返回 200。随后在 AdGuard Home 中启用 DoH 上游,实际访问恢复。

但我们没有抓到污染发生的具体一跳,所以不能把责任直接归给某台路由器、某家运营商,也不能把“网站跳转”全部诊断成 DNS 污染。本文把已验证的事实、技术推断和操作建议分开讲清楚。

一、没有改过网站,为什么打开后成了百度?

这次故事的起点非常简单:打开自己的博客,地址栏最后却去了百度移动版。第一反应自然是:网站被黑了?域名过期了?静态页面里混进了跳转脚本?还是 DNS 被污染了?

博客使用 Hugo 生成静态页面,托管在 GitHub Pages,公开博客域名通过 CNAME 指向 Pages 的站点名称。平时访问它,就像给一个熟悉的商店打电话:你只记住店名,通讯录替你查号码。

然而,通讯录上的号码错了,并不代表真正的商店关门了。 你打到另一个地方,对方再说“请去隔壁”,就同时出现了两个问题:第一次是找错地址,第二次是错误地址上的服务给出了跳转指令。

原创插图:错误地址簿把博客访问带到了另一个门口

图 1:本文原创 SVG。它是解释事故的示意图,不是现场截图。后文的官方资料截图也会明确标注来源。

二、先拆开三个动作:问路、敲门、看证件

把一次网站访问想成去同学家做客,会容易理解得多。

DNS 是问路。 你说出同学的名字,得到一个门牌号。A 记录提供 IPv4 地址,AAAA 记录提供 IPv6 地址;CNAME 像一句“这个名字也叫另一个名字”,还要继续查到最终地址。自定义域名自身看起来没问题,不代表后面的别名解析一定没问题。

HTTP 是敲门之后的对话。 门里的人说“欢迎进来”,可能对应 200;说“临时去那个地方”,就是 302 加上 Location。DNS 返回的是解析结果,它本身不会发送 HTTP 302。实际跳转由浏览器收到的 HTTP 响应触发;脚本跳转和 HTML 刷新则是其他机制,需要另行检查。

HTTPS 还要看证件。 即使门牌号告诉你到了这里,门里的人也要出示能证明“我就是这个域名”的有效证书。证书不匹配时,正确行为是阻止连接。不要为了“先看看网页”把证书检查关掉,那会恰好拆掉识别冒名者的门锁。

原创流程图:DNS、服务器连接与 HTTP 响应是不同阶段

图 2:地址错误与重定向是连续发生的两件事。HTTPS 的证书校验可以在请求正常完成前拦住错误身份。

MDN 官方 HTTP 302 文档真实截图

图 3:真实浏览器截图,来源为 MDN:302 Found。302 表示临时重定向,目标由 Location 指定。

三、这次到底查到了什么?一张不泄露地址的证据表

排障时,没有先修改配置,而是分别看原路径、独立解析和指定正确地址后的访问。公开记录中移除了所有完整 IP、设备名称和内部网络信息,只保留足以理解结论的现象。

检查 实际结果 能说明什么
系统 DNS 与两条局域网解析路径 返回同一个异常 IPv4 地址 错误并非只存在于单个浏览器页面
AAAA 查询 正常结果中混入异常 IPv6 记录 不能只盯着 IPv4
原路径 HTTP 请求 302 Found,目标为百度移动版 直接复现了跳转
原路径 HTTPS 请求 证书域名不匹配 到达的端点无法证明自己就是博客
两家独立 DoH 服务 返回一致的 GitHub Pages IPv4 地址集合 得到了独立对照结果
其中一家 DoH 的 AAAA 查询 返回正常 Pages IPv6 地址集合 原有 IPv6 结果也需要纠正
保留域名、指定正确地址访问 HTTPS 证书通过,200,标题为预期博客标题 正确站点仍能正常提供内容
获取正确站点首页内容 未发现百度目标字符串或 meta refresh 所检查首页未提供这种跳转证据
改用 AdGuard Home DoH 上游 用户确认恢复正常 完成了实际体验层的验证

最后那条用户确认很重要。终端显示 200,不代表手机浏览器已经清掉旧缓存,也不代表另一台设备确实使用了同一个 DNS。配置正确、接口正常和用户能打开页面,是三件有关联但不等价的事。

原创证据链:原路径、独立解析和保持域名的对照请求

图 4:多个对照共同支持“原解析链路异常”。它们没有定位到中间哪一台设备改写或缓存了答案。

为什么指定地址时还要保留域名?

因为直接把地址填进浏览器,会改变 HTTP Host 和 TLS 所验证的名称。共享托管服务靠域名分辨你要访问哪个网站,这样测试很容易把正确服务也测成错误。

curl --resolve 可以只替换连接地址,继续使用原域名做 SNI、证书验证和 HTTP 请求。下面是模板,变量值必须来自你的实际检查;不要照抄网上陌生地址:

# BLOG_HOST:你正在排查的公开域名
# VERIFIED_ADDRESS:通过独立可信渠道核对过的地址
# 若使用 IPv6,按 curl 要求为地址加方括号
curl --noproxy '*' --connect-timeout 5 --max-time 20 \
  --resolve "${BLOG_HOST}:443:${VERIFIED_ADDRESS}" \
  -I "https://${BLOG_HOST}/"

HEAD 请求能快速看响应头,但网站可能对 HEAD 和 GET 有不同处理,最终还要获取页面正文并打开浏览器。--noproxy '*' 用于明确测试直连路径;如果你的正常访问依赖代理,也要把代理路径作为另一组对照,不能混在一起比较。

GitHub Pages 自定义域名官方文档真实截图

图 5:真实浏览器截图,来源为 GitHub Pages 自定义域名文档。共享托管下,连接地址和域名身份需要同时正确。

四、为什么换成“知名 DNS”还不行?

这次还有一个很有辨识度的现象:向两家常见公共 DNS 地址发送普通 UDP 查询,依然收到了与原路径相同的异常答案;而对其中一家改用 TCP 查询,得到了正常的 Pages 地址。

这说明,在命令里写了谁的地址,不等于答案一定由谁原封不动地送回来。 中间路径可能存在透明转发、劫持、错误缓存或其他改写行为。这个现象与 DNS 污染或劫持相符,但单靠这些输出,仍然不能判定具体设备和责任主体。

生活中的比喻是:你换了一家商店打电话,但电话线路里一直有人插话。换号码可能没用,保护这段通信才是关键。

TCP 在这次对照中有用,却不因此变成安全协议。传统 DNS over TCP 同样不提供 TLS 那样的机密性和服务端认证,不能把“这一次正常”当作长期防污染保证。

DoH 改变了什么?

DoH 把 DNS 查询和响应装进 HTTPS。与其在街上喊“谁知道这家店在哪里”,不如给已经核验身份的问路处递一个加密信封:路上的人更难读取或修改内容,冒充问路处还必须通过证书验证。

但信封只保护传递过程,不保证收信人永远正确。选择的解析器仍然可以看到查询,也可能配置错误、执行过滤策略或返回不合适的答案。DoH 不等于匿名工具,也不是网站被入侵、权威记录配错或客户端恶意软件的通用修复药。

RFC 8484 官方标准真实截图

图 6:真实浏览器截图,来源为 RFC 8484:DNS Queries over HTTPS。标准定义的是 DNS 查询与响应如何映射到 HTTPS 交换。

五、AdGuard Home 有两个“加密方向”,不要打开错开关

这是本次最值得留下来的操作知识:

设备 ── 接入侧 DNS ──> AdGuard Home ── 上游 DNS ──> 递归解析器

第一段是设备向 AdGuard Home 提问。第二段是 AdGuard Home 不知道答案时,向上游提问。本次修复重点是第二段:在 DNS 设置的上游 DNS 服务器中使用 https://…/dns-query 形式的地址。

管理界面的 加密设置通常是在配置 AdGuard Home 自己对外提供 HTTPS、DoH、DoT 等服务,需要考虑证书、监听端口和客户端接入。只在这里打开加密,而第二段继续使用异常的明文上游,问题未必消失。

原创拓扑:客户端接入侧与解析器上游侧

图 7:两段通信可以分别配置。本次不能把“使用 DoH 上游”误写成“必须给家庭里的每台设备安装证书”。

AdGuard Home 加密知识库真实截图

图 8:真实浏览器截图,来源为 AdGuard Home DNS 加密文档。此页主要说明如何提供加密 DNS 服务,正好帮助区分两个方向。截图采集时官网按浏览器语言显示中文。

六、人工配置:按这六步走,别只点“保存”

第一步:记录旧配置

保存原来的上游、备用上游、Bootstrap、按域名分流规则,以及客户端自定义上游。配置备份只留在自己的私有目录,别上传到公开仓库,也别把整页管理后台截图直接发出去。若使用上游配置文件,应沿用原有配置管理流程。

第二步:设置经过测试的 DoH 上游

本次对照中,阿里和腾讯的加密解析入口可用。对应常用 DoH 配置如下,保存前仍应在你自己的 AdGuard Home 里点击测试:

https://dns.alidns.com/dns-query
https://doh.pub/dns-query

这不是测速排行,也不是保证所有地区都可用。本次其他部分境外加密入口出现连接超时,说明“协议更安全”和“这条网络能连通”要分别验证。可以使用自己的 DoH 服务,只要证书、可达性和解析结果都经过检查。

第三步:理解 Bootstrap,而不是乱填一串地址

DoH 服务自己也是域名。AdGuard Home 第一次连接它之前,往往需要先查出它的地址,Bootstrap 就像“先告诉我问路处在哪里”。保留或填写本地验证可用的引导解析器,不要把引导查询又绕回依赖它启动的服务,造成鸡生蛋、蛋生鸡的死循环。

引导环节得到错误地址时,正常 TLS 校验通常会阻止冒名连接,但服务也可能因此不可用。不要把证书校验关闭当作解决办法。本文不刊载完整数字地址,实际引导地址请从官方资料核对后在私下配置。

第四步:处理备用路径

如果主上游改成 DoH,备用上游却仍然是那条异常明文路径,那么主服务稍有抖动,问题就可能回来。小型个人部署可以清空不需要的备用项,或填写另一个已经验证的加密上游。业务系统则应先评估可用性要求,不能盲目删除既有容灾设计。

第五步:保存、清缓存,再检查 IPv6

清除 AdGuard Home DNS 缓存,也刷新客户端缓存并重启浏览器。Windows 可使用 Clear-DnsClientCache;Ubuntu 在使用 systemd-resolved 时可运行 sudo resolvectl flush-caches;macOS 可运行:

sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

这些命令只刷新当前系统相关缓存,不会神奇地清掉所有浏览器、路由器和应用缓存。

确保 DHCP 下发的 DNS 指向预期服务;同时检查 IPv6 的 RA/DHCPv6,以及浏览器“安全 DNS”和 VPN 是否另走一条路。多个 DNS 地址不一定严格按照“主用坏了才用备用”的顺序工作,不要把异常地址留在列表里当作装饰。

原创插图:IPv4、IPv6 与浏览器解析器构成不同入口

图 9:关闭 IPv6 不是默认答案。更好的做法是把两种地址族的解析与客户端路径都检查完整。

第六步:验收真实访问

重新查询 A 和 AAAA,查看 AdGuard Home 查询日志是否收到这台设备的请求、使用了什么上游、是否命中旧缓存。随后使用正常浏览器访问原来的 HTTPS 地址,确认没有证书警告、没有跳往无关页面,正文确实是预期网站。必要时换第二台设备复测。

AdGuard Home 配置知识库真实截图

图 10:真实浏览器截图,来源为 AdGuard Home Configuration。网页设置和配置文件会随版本演进,操作前应核对正在使用的版本。

七、三系统一键脚本:把重复操作交给程序,把结果留给验证

本文提供 完整离线脚本包,包含 Windows 11、Ubuntu 26.04、macOS 26 三种启动脚本,以及共享的 configure_doh.py 核心。也可单独下载:

依赖边界说清楚:需要预先安装 Python 3.10 或更新版本,只用标准库,不需要 pip、云端 Agent、在线脚本托管或其他自动化服务。 macOS 和 Windows 不保证自带 Python,脚本会检查并停止,不会擅自下载运行环境。使用公共 DoH 当然仍然依赖那家解析服务;若要求不使用第三方递归服务,请提供自己的 DoH 入口,但自建递归本身仍有访问权威 DNS 的网络需求,不能宣称“从此完全离线解析互联网”。

脚本适用于已经安装、初始化、可管理的 AdGuard Home,自动完成:API 预检 → 服务端测试候选上游 → 私有备份 → 替换全局上游并清空备用上游 → 读回对比 → 再测上游 → 清缓存。失败时尝试恢复旧值并验证,失败退出码不为零。

它不会安装 AdGuard Home,不修改路由器 DHCP 或系统网卡 DNS,也不覆盖 Bootstrap、客户端专属规则。发现按域名分流或文件管理的上游时会停止,避免破坏现有部署。运行时应避免另一名管理员同时修改配置。Windows 备份目录应使用当前用户独享的 ACL;备份可能含私人解析地址,不能公开。

原创自动化流程:预检、备份、应用、读回与回滚

图 11:脚本的成功标记只表示目标配置已经应用并读回,不代表全网设备已完成迁移。

方法 A:人来执行,程序自动配置

解压脚本包,在所在目录执行对应命令;按提示在本机输入管理 URL、账号、DoH 地址,密码不会回显。管理 API 必须使用证书可信的 HTTPS;只有通过可信 SSH 隧道连接本机回环时才允许 HTTP,不允许关闭 TLS 校验。

# Windows 11:已安装 Python 及 py 启动器
powershell -NoProfile -File .\windows11.ps1 --apply
# Ubuntu 26.04
bash ./ubuntu26.sh --apply

# macOS 26
bash ./macos26.sh --apply

不加 --apply 只做配置读取和候选上游测试,不保存变更。遇到 PowerShell 执行策略限制,应审阅脚本后按本机政策处理,不建议为了运行教程永久放宽系统策略。

方法 B:Agent 自动配置

在私有目录创建 plan.json,结构如下。花括号内容必须替换成真实值,模板不能原样执行,也不要把真实配置提交进 Git:

{
  "base_url": "https://{你的管理入口}",
  "username": "{管理账号}",
  "upstreams": [
    "https://dns.alidns.com/dns-query",
    "https://doh.pub/dns-query"
  ]
}

密码单独保存在当前用户独享的文件中。Linux/macOS 设置文件权限为仅当前用户可读写;Windows 使用相应文件 ACL。私有 CA 可用可选字段 ca_file 指定,不得改成忽略证书。随后让本地 Agent 执行:

python3 configure_doh.py --plan plan.json --password-file password.txt
python3 configure_doh.py --plan plan.json --password-file password.txt --apply

Windows 使用 py -3 替换 python3。脚本没有内置任何云端模型依赖;若使用外部 Agent 服务,不要把密码或未脱敏日志放进对话。可以给 Agent 这段任务约束:

只操作指定的 AdGuard Home。先读脚本和计划文件,只返回脱敏摘要;预检成功后应用,保存私有备份。不要修改客户端网络、Bootstrap、分流和文件管理配置,不要输出密码、完整地址或设备名称。检查退出码,随后单独核验 A/AAAA、查询日志、证书和浏览器页面。如果无法操作浏览器,明确报告“配置已应用,端到端验证待完成”。

需要恢复时,从私有备份目录找到本次 JSON,使用同一计划和凭据运行:

python3 configure_doh.py --plan plan.json --password-file password.txt \
  --restore "{本次备份文件路径}" --apply

恢复前会检查当前目标字段是否仍等于当时应用的值;如果后来有人改过,脚本拒绝覆盖。恢复旧上游也可能恢复原来的故障,所以它是撤销工具,不是另一种修复。

验证范围:共享核心通过本地模拟 API 的成功、拒绝和回滚测试,Bash 入口经过语法检查及当前 macOS 调用检查;本文没有声称已在真实 Windows 11、Ubuntu 26.04 和三套生产 AdGuard Home 上全部实测。 API 内置上游测试仅证明测试查询成功,不能保证所有域名、所有客户端都正常。

八、Q&A:几个最容易把人带偏的问题

1. 跳到百度,能证明是百度做的吗?

不能。Location 指向哪里,只说明客户端被要求去哪里。错误服务器、拦截设备或其他组件都可以返回这样的目标,目的地址不是责任归属证据。

2. 两家 DNS 的结果不同,就一定是污染?

也不一定。CDN、地域调度、缓存时间和 ECS 都可能产生正常差异。本次判断依赖的是一整组事实:异常地址、错误重定向、证书不匹配,以及保持域名后的有效 HTTPS 对照。不要拿“地址不一样”这一个现象就下结论。

3. HTTPS 不是很安全吗,为什么还有这件事?

这次 HTTP 路径会跳转,HTTPS 路径则因证书不匹配而失败,后者恰恰是在保护用户。HTTPS-first 和 HSTS 可以减少走到明文 HTTP 的机会,但不会自动修好 DNS 地址簿。用户如何输入地址、浏览器的升级策略和既有 HSTS 状态都会影响最终表现。

MDN HSTS 文档真实截图

图 12:真实浏览器截图,来源为 MDN:Strict-Transport-Security。HSTS 约束 HTTPS 访问,不能替代 DNS 排障。

4. 能不能写 hosts 一劳永逸?

可以作为临时对照,但不建议当作长期答案。托管平台可能调整地址、路由或部署。把今天的门牌号永久钉死,明天可能又走错路。正确做法是修复可信解析路径,并根据官方要求维护域名记录。

5. DNSSEC 和 DoH 是不是一回事?

不是。DoH 主要保护客户端与所选解析器之间的传输;DNSSEC 在签名和验证链完整时验证 DNS 数据的来源与完整性。可以把它们分别理解为“密封运输袋”和“文件上的可验证签章”。没有签名的数据不会因为开启验证就自动获得签名,两者也都无法修复网站自身的恶意跳转代码。

6. 为什么我的电脑好了,手机还不好?

先查手机是不是走蜂窝网络、IPv6 下发的另一台解析器,或者浏览器内置的安全 DNS。再查缓存。不要第一步就恢复出厂设置,也不要用“后台全绿”代替设备侧观察。

7. 开了广告过滤,为什么仍然会中招?

广告过滤决定哪些请求被允许或拦截,上游加密决定允许的查询怎样送出去。允许访问的域名仍可能被错误解析。此次解决问题的是改变上游解析路径,不是新增广告规则。

九、这次真正修好的,不只是一个跳转

最后的验收标准很朴素:同一个网址,用正常的浏览器打开,身份验证正常,页面内容正确。经过上游 DoH 配置后,用户确认问题消失,排障闭环才算完成。

原创验收图:配置、解析、用户体验三层检查

图 13:每一层都要有自己的证据。“保存成功”不能替代“页面正确”。

这类故障最容易让人一上来就重装网站、乱改域名记录或关闭安全检查。更省事的方法,是先问三个问题:地址是谁告诉我的?我实际连到了谁?对方有没有证明自己的身份? 把这三件事拆开,很多看起来像灵异事件的跳转,就会变成可以逐项验证的工程问题。

参考资料与配图说明

资料核对与截图采集于 2026 年 9 月 17 日。本文 13 张配图包括 6 张官方网页真实截图和 7 张原创 SVG;没有把排版后的文字伪装成终端截图,也没有把事后截图说成事故发生时的现场录像。引用网页截图用于对应技术点的说明,内容版权属于原作者。

  1. AdGuard Home:Configuration:上游、引导和配置项说明。
  2. AdGuard Home:DNS encryption:作为加密 DNS 服务端的配置。
  3. AdGuard Home OpenAPI:本文脚本使用的 DNS 配置、上游测试、缓存清理接口;部署版本可能与主分支有差异。
  4. RFC 8484:DoH 协议。
  5. MDN:302 Found:临时重定向语义。
  6. MDN:Strict-Transport-Security:HSTS 与证书错误行为。
  7. GitHub Pages 自定义域名:托管域名与 DNS 配置。

延伸阅读:DNS 的历史、架构与加密防护GeoIP、CDN 与 HTTP 重定向Read this article in English

本文阅读量 --