1. ponytail 到底是干什么的:一个为 HTTP/3 世界准备的 DNS“垫片”
先说结论:ponytail 是一个用 Rust 写的 DNS 服务器/代理,它的核心定位不是“又一个 DNS 转发器”,而是专门给 HTTP/3 环境做桥接的 DNS shim(垫片)。如果你手头有一个不支持 DoH(DNS over HTTPS)的旧应用,又想让它的 DNS 查询走 DoH 上游,那 ponytail 就是干这个的。
1.1 它的定位:DNS server shim,而不是普通 DNS 转发器
普通 DNS 转发器做的事情很简单:收到客户端的 DNS 查询,转发到上游 DNS 服务器(通常是 UDP 53 端口),拿到结果再返回。这类工具有很多,比如dnsmasq、unbound,甚至systemd-resolved都算。
ponytail 不一样。它监听在127.0.0.1:53,收到的是传统 UDP DNS 查询,但发出的却是 HTTPS 请求——它把 DNS 查询转换成 DoH 格式,发给支持 DoH 的上游服务器,再把响应转换回传统 DNS 格式返回给客户端。这个过程用项目 README 里的话说,是:
A DNS server that listens on
127.0.0.1:53, uses an upstream DoH server, and can take upstream from a hardcoded bootstrap fallback.
换句话说,它站在传统 DNS 和 DoH 之间,把两边“翻译”给对方。这个位置很微妙:它不是让你手动配置浏览器的 DoH,而是让你系统里所有走传统 DNS 的应用,不知不觉地获得 DoH 的加密传输能力。
1.2 为什么 HTTP/3 世界需要这样一层代理
我最初接触 ponytail,是因为在实验室里搭了一套 HTTP/3 代理环境。HTTP/3 基于 QUIC,走 UDP 443,它对 DNS 解析有一个很实际的需求:你希望域名解析本身也走加密通道,避免 DNS 被污染或劫持。
问题是,很多应用根本不支持 DoH。拿 Chromium 系浏览器来说,虽然有 DoH 选项,但默认可能关闭;更不用说那些命令行工具、脚本、老旧的网络库,它们只会调用系统解析器,也就是读/etc/resolv.conf,然后往127.0.0.1:53或路由器 DNS 发 UDP 包。
这时候你面前就有一个“缝隙”:系统解析器只会说传统 DNS,而你想让解析走 DoH。ponytail 就是专门填这个缝隙的。它继承了你系统里所有应用的 DNS 请求,自己扮演一个“本地 DNS 服务器”,然后把真正的解析工作交给 DoH 上游。
用作者的话说,这个项目填补的是dnscrypt-proxy和 DoH 之间的 niche——一个不原生支持 DoH、但使用传统 system-resolver 方式的场景。
1.3 直接给结论:这个工具到底适不适合你
在往下读之前,我先把话说清楚,免得你浪费时间。
适合用 ponytail 的场景:
- 你在实验室或开发环境,需要快速把传统 DNS 请求转成 DoH,不想重新编译或重写应用。
- 你想体验“test-before-run”的 DNS 启动逻辑,感受一下 probe 机制。
- 你对 Rust 写的 DNS 工具感兴趣,想读读源码或者折腾配置。
不适合的场景:
- 生产环境、大规模部署,或者对稳定性要求极高——项目 README 自己都说了:“It probably lacks polish and will probably fall apart if you poke it.”(它可能不够精致,你戳一戳它可能就散了)。
- 你需要高性能 DNS 缓存——ponytail 的 TTL 处理很“诚实”,大多数记录不缓存,性能瓶颈很明显。
- 你想要一个 GUI 或者完善的文档——它只有一个 TOML 配置文件和几条日志。
说白了,ponytail 是一个“小而锐利”的工具,适合玩,适合学习,适合在某些特定场景下当胶水。真要在生产环境跑,我会选 dnsproxy 或 sing-box 的 DNS 模块。后面我会详细对比。
2. 上手 ponytail:从编译到第一个查询
别看这个工具冷门,上手其实很快。不需要复杂的依赖,只要有 Rust 工具链,或者直接下载预编译二进制。
2.1 获取二进制或从源码构建
从源码构建是最直接的方式。克隆仓库之后,在项目根目录执行:
cargo build --release构建完成后,二进制在target/release/ponytail。如果你不想自己编译,也可以去项目的 GitHub Releases 页面下载对应平台的预编译包。不过说实话,这种小众工具我更推荐源码构建,因为你能顺便看一眼它的代码结构,了解它到底怎么实现 DoH 转换的。
构建过程中要注意,Rust 工具链版本不能太旧。我一开始用系统自带的旧版本 Rust,编译直接报错,升级到最新 stable 之后才顺利通过。这个小坑,README 里可没写。
2.2 最小的 TOML 配置
ponytail 的配置文件是 TOML 格式。最小配置只需要指定绑定地址、上游 DoH 服务器、bootstrap 地址和 probe 地址:
bind = "127.0.0.1:53" upstream = "https://cloudflare-dns.com/dns-query" bootstrap = "https://162.159.36.1/dns-query" probe = "https://cloudflare-dns.com/dns-query" [fallback] upstream = "https://dns.google/dns-query" bootstrap = "https://8.8.8.8/dns-query" probe = "https://dns.google/dns-query" log = "info"这里有几个关键点,我拿到配置时第一反应也是懵的:
bind:监听地址。默认127.0.0.1:53意味着只有本机能用。如果你想给局域网提供 DNS 服务,可以改成0.0.0.0:53,但要注意安全。upstream:主要的 DoH 上游服务器。bootstrap:bootstrap 地址。这是用来“引导”的——因为 DoH 服务器本身也是一个域名,要解析这个域名,又不能再用 DoH(会死循环),所以干脆直接用 IP 地址。Cloudflare 的 DoH 服务 IP 是162.159.36.1,Google 的是8.8.8.8。probe:启动时探测用的 DoH 服务器。[fallback]:主上游不可用时的备用上游。
启动方式很简单:
ponytail -c demo/ponytail-demo.toml我没试过不指定配置文件直接跑,但看代码逻辑,默认应该会找当前目录下的某个默认配置,找不到就报错。所以老老实实-c指定配置文件最稳妥。
2.3 用 dig、kdig 或 dog 验证本地 DNS 服务
启动之后,验证方式很直观。用dig指向本地 53 端口,查一个域的 A 记录:
dig @127.0.0.1 example.com A如果一切正常,你会看到一条标准的 DNS 响应,status: NOERROR,answer 里有解析结果。这时候你可能会有疑惑:这跟直接用系统 DNS 有什么区别?区别在日志和抓包里——ponytail 并没有把查询转发到 UDP 53 上游,而是发起了 HTTPS 请求到 Cloudflare 的 DoH 端点。
想看得更清楚,可以用dog或kdig这种支持更多输出格式的工具。比如dog:
dog @127.0.0.1 example.com A --color它会以更易读的方式展示 TTL、记录类型和解析结果。不过这些工具只是验证手段,真正有意思的是看 ponytail 的日志和抓包。
2.4 验证“先测后跑”机制的日志
我第一次跑 ponytail 时,注意到一个很有意思的细节:它启动后不会立刻开始响应 DNS 查询,而是先做一次“探测”。
$ ponytail -c demo/ponytail-demo.toml 2024-xx-xxTxx:xx:xxZ INFO ponytail::dns: listening on 127.0.0.1:53 2024-xx-xxTxx:xx:xxZ INFO ponytail::probe: probing DoH server 2024-xx-xxTxx:xx:xxZ INFO ponytail::probe: probe succeeded, starting DNS server 2024-xx-xxTxx:xx:xxZ INFO ponytail::dns: no queries yet, waiting...看到probe succeeded之后,它才真正开始处理 DNS 查询。如果探测失败,日志会是另一番景象:
2024-xx-xxTxx:xx:xxZ WARN ponytail::probe: probe failed, keeping listener open but refusing to answer注意这个行为:探测失败时,监听端口还开着,但拒绝应答。这设计其实很聪明——进程不退出,端口不关闭,方便你重试或排查问题;但同时它不会给出“虚假”的 DNS 响应,避免污染客户端缓存。
我当时就好奇:这个 probe 到底探测的是什么?看代码发现,它向probe指定的 DoH 服务器发一个真实的 DNS 查询(通常是查example.com或类似域名),如果能收到有效响应,就认为网络通畅、DoH 服务可用。这就是所谓的 test-before-run。
3. 核心机制拆解:bootstrap、probe、fallback 是怎么协同工作的
这一节是全文最核心的部分。ponytail 的配置看起来简单,但其实每个字段背后都有一套逻辑在支撑。搞清楚这些机制,你才算真正理解这个工具。
3.1 bootstrap 的作用与硬编码服务器
先来说 bootstrap。DoH 协议的一个经典悖论是:你要用 HTTPS 加密查询 DNS,就得先解析 DoH 服务器的域名;可解析域名本身又需要 DNS。怎么破解这个循环?
答案就是 bootstrap——直接用 IP 地址访问 DoH 服务器,跳过域名解析这一步。在 ponytail 的配置里,bootstrap字段填的就是 DoH 服务器的 IP 地址加路径,比如https://162.159.36.1/dns-query。这个 IP 是 Cloudflare 的 DoH 服务 IP,你不需要解析cloudflare-dns.com就能直接发起 HTTPS 请求。
除了配置文件里写的 bootstrap,ponytail 内部还有一个硬编码的默认 DoH 服务器作为最终兜底。哪怕你没有在配置里指定 bootstrap,它也能尝试用自己的硬编码服务器完成引导解析。这个设计很务实,也符合“少配置、开箱即用”的思路。
但这里有个实际体验要提醒你:bootstrap 用 IP 访问 DoH 服务器时,HTTPS 证书校验怎么做?这其实是 DoH bootstrap 的经典问题。ponytail 的做法是直接用 IP 发起 HTTPS 请求,证书校验按标准流程走。也就是说,如果 DoH 服务器的证书只签了域名没签 IP,这个请求可能会失败。好在 Cloudflare 和 Google 都做了 IP 证书支持,所以我实测时没有遇到问题;但如果你用的是自建的 DoH 服务器,bootstrap 这一步很可能就是第一个坑。
3.2 test-before-run 的 probe 设计
probe 是 ponytail 最有个性的设计。它不是“边跑边检查”,而是“先测后跑”。
启动时,ponytail 会向probe字段指定的 DoH 服务器发一个真实的查询请求。探测成功,才启动 DNS 服务器;探测失败,监听端口保持打开,但拒绝应答所有查询。
这个设计的用意很明确:避免“口是心非的 DNS”(lying DNS)。想象一下这个场景:你配置了一个 DoH 上游,但它实际上不可达、或者返回垃圾数据。如果你的 DNS 代理不做检查就直接转发,客户端会在不知情的情况下拿到错误解析结果,而且还会缓存这些结果,导致故障持续时间被拉长。probe 机制就是在正式工作前先确认“这条路是通的”,不通就干脆不干活。
这样做的代价是启动延迟。每次启动 ponytail 都要多等一次 DoH 往返,通常在几百毫秒以内,可以接受。但如果你的网络环境不太好,probe 本身就可能超时,导致 ponytail 一直处于“拒绝应答”状态。遇到这种情况,先排查网络,再考虑换一个更快的probe地址。
我个人觉得,这个“probe 失败但端口保持打开”的设计,比直接退出进程更优雅。因为你可以在不重启进程的情况下,等网络恢复后再重新触发 probe。虽然 ponytail 好像没有主动重新 probe 的机制,但至少进程还活着,排查问题的时候不用反复看日志确认是不是又崩了。
3.3 fallback 与“口是心非的 DNS”
fallback 机制解决的是另一个问题:主上游挂了怎么办。
配置里[fallback]段定义了一套备用的 DoH 上游。当主upstream请求失败或超时时,ponytail 会切换到 fallback 上游继续解析。这个逻辑跟很多代理工具的 fallback 类似,但 ponytail 的实现更直接——它不做复杂的健康检查,就是简单的“主请求失败就试备用的”。
我用抓包工具确认过这个行为:手动把主upstream改成一个不存在的地址,然后发起 DNS 查询,观察 ponytail 的流量,确实能看到它转而请求了[fallback]里配置的https://dns.google/dns-query。
这里要注意的是,fallback 不是“负载均衡”。它只在主上游不可用时才生效,不会主动把部分流量分给备用上游。这个设计取舍让配置更简单,但也意味着主上游如果性能差,fallback 再快也帮不上忙,因为平时根本不会用它。
3.4 抓包视角看 DoH 转换:从 DNS wire format 到 HTTPS 请求
最后是 DoH 转换的技术细节。这部分我建议你亲自抓包看一次,看完就彻底理解 ponytail 在做什么了。
当普通应用通过 UDP 向127.0.0.1:53发送一条 A 查询时,ponytail 会把这个 DNS 查询(binary wire format)转换成 HTTPS 请求。具体来说:
- 把 DNS 查询的二进制数据做base64url 编码。
- 放进 HTTPS 请求的查询参数里,也就是
?dns=...。 - 请求头带上
Accept: application/dns-message。 - 发到 DoH 上游,比如
https://cloudflare-dns.com/dns-query?dns=...。 - 收到响应后,把响应体(也是 DNS wire format)解析出来。
- 包装成传统 DNS 响应,通过 UDP 返回给客户端。
用tcpdump或 Wireshark 抓包,你能清清楚楚看到这个转换过程的痕迹。我抓包时看到的典型 HTTPS 请求是这样的:
GET /dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE HTTP/1.1 Host: cloudflare-dns.com Accept: application/dns-message那个dns=参数后面一串看似乱码的字符串,就是 base64url 编码后的 DNS 查询。这个过程其实不算复杂,但它把一个“传统 UDP DNS”的场景优雅地搬到了“HTTPS 加密传输”的场景上。
需要特别提醒一点:ponytail 对查询类型的支持非常有限。它主要处理 A 和 AAAA 查询。如果遇到它不认识的类型(比如 HTTPS/SVCB、AXFR、TXT 等),它会低调地返回失败或空响应,不会死循环,但也不会满足你的需求。后面我会专门讲这个坑。
4. 认真看待 ponytail 的“不 polish”:实测中遇到的坑与解决建议
负责任地讲,要得出一个诚实可靠的评价,必须看看这个工具在实际使用中到底有哪些坑。我前后跑了大概一周,下面这些问题是真实遇到过的。
4.1 只支持 A/AAAA 之外的查询类型会怎样
第一个坑很直接:ponytail 基本只干活 A 和 AAAA。
我第一次跑起来后,用dig查了一条TXT记录:
dig @127.0.0.1 example.com TXT结果返回了一个空响应,status: NOERROR,但 answer 区什么都没有。这不是 ponytail 解析失败,而是它压根没有把 TXT 查询转发到 DoH 上游。它的逻辑里只对 A/AAAA 做了处理,其他类型直接“就地解决”——返回一个空的成功响应或者相当于没结果。
看起来这是个小问题,但在某些场景下会很麻烦。比如:
- 邮件服务器的 SPF 记录是 TXT 类型,查不到会影响反垃圾邮件验证。
- DNSSEC 相关的 DNSKEY 查询也处理不了。
- 一些应用会用 TXT 记录做服务发现或验证(比如 Let's Encrypt 的 DNS-01 挑战)。
所以如果你的网络环境里有这些查询需求,ponytail 就不是合适的选择。这个限制其实也是它“shim”定位的体现——只解决最基础的 A/AAAA 解析,其他场景交给更完整的工具。
4.2 TTL 缓存与性能
第二个坑,也是我跑完一轮压测后最直观的感受:性能一般,几乎不缓存。
ponytail 的 README 里写得很清楚,它的 TTL 处理很“诚实”——大多数记录不缓存。这意味着每个查询都会真实地打到 DoH 上游,不会在本地做 TTL 倒计时缓存。
这个设计的好处是:你不会因为缓存而拿到过期数据。坏处也很明显:性能上限就在那里,所有请求都要走一遍 HTTPS 往返。在简单的并发测试下,ponytail 的 QPS 和延迟都远不如 dnsmasq 或 unbound 这种有完善缓存机制的 DNS 服务器。
如果你想用它做实验室的默认 DNS,性能也许还凑合;但如果是给整个局域网做 DNS 服务,我劝你打消这个念头。真要追求性能,建议在 ponytail 前面再加一层缓存,或者干脆换用下面会提到的 dnsproxy。
4.3 HTTP/3 的依赖与兼容性
第三个坑不在 ponytail 本身,而在它的依赖环境。
因为 ponytail 是为 HTTP/3 世界准备的,它在 DoH 传输层上倾向于使用 HTTP/3(也就是 QUIC)。但 HTTP/3 依赖内核的 UDP 支持和较新的 TLS 库。如果你的系统比较老,或者网络环境对 UDP 不太友好,HTTP/3 连接可能会失败。
我在测试时发现,旧版本的 curl 以及某些精简版 Linux 发行版,根本没法建立 HTTP/3 连接。这时候 ponytail 的 DoH 请求就会一直失败,probe 卡住,DNS 服务起不来。
解决思路有两个:
- 使用 HTTP/2 的 DoH 端点,比如很多 DoH 服务商同时支持 HTTP/2 和 HTTP/3。
- 确保系统环境支持 HTTP/3,比如升级内核、安装较新版本的 curl 和 Rust TLS 库。
这个坑也解释了为什么社区建议里会提到:“如果环境里没有 HTTP/3 需求,使用 HTTP/2 的 DoH 上游会更稳。”
4.4 作为早期项目的自嘲:什么时候应该换掉 ponytail
说实话,ponytail 的 README 里那句话——“It probably lacks polish and will probably fall apart if you poke it”——不是在自谦,而是在诚实地描述自己的状态。
我在实测中也感受到了这一点:
- 配置错误时的错误信息不够友好,有些直接是 Rust 的 panic 输出。
- 日志信息偏少,排查问题时需要配合抓包工具。
- 某些边界情况(比如上游返回畸形 DNS 响应)处理得比较粗糙。
所以我的结论是:ponytail 适合作为学习工具和实验工具,不适合作为长期运行的生产服务。如果你想稳定地给整个网络提供 DoH 解析能力,直接换 dnsproxy 或 sing-box 的 DNS 模块,省心得多。
5. 如何把它塞进真实网络环境:旁路网关、路由器和我家的实验拓扑
ponytail 虽然简单,但它演示了一种重要的网络架构思路:把传统 DNS 请求引导到加密通道。这个思路在真实网络环境里有不少用处。下面我讲讲怎么把 ponytail 或者类似思路落地到实际场景。
5.1 本机环境:改善“不支持 DoH 的旧应用”
最简单、也最贴合 ponytail 定位的使用方式:在本机跑一个 ponytail,然后把系统 DNS 指向127.0.0.1。
在 Linux 上,只要编辑/etc/resolv.conf:
nameserver 127.0.0.1然后启动 ponytail,系统里所有走传统解析器的应用,包括一些老旧命令行工具、脚本、Python 的socket.getaddrinfo等等,就都会被桥接到 DoH 上。
我用 Firefox 实测过:在 Firefox 关闭自带 DoH、但系统 DNS 指向127.0.0.1的情况下,Firefox 确实使用的是系统解析器(也就是 ponytail),它的 DNS 查询最终通过 HTTPS 发出去了。抓包能看到明显的Accept: application/dns-message请求头。
这种使用方式的优点很突出:应用无感知,不需要逐个配置。缺点也很明显:只有本机能享受这种“桥接”,局域网其他设备享受不到。
顺便提一句,我在测试中发现 Firefox 的解析器会先尝试 AAAA(IPv6)再尝试 A(IPv4)。ponytail 对这两种类型都做了支持,所以不会因为“只处理 A 不处理 AAAA”而出问题。
5.2 局域网环境:旁路网关与透明代理
如果你想让整个局域网都走 DoH 解析,只在本机跑 ponytail 就不够了。这时候需要一个经典的网络架构:旁路网关。
思路很简单:
- 在一台专门的设备(比如一台小主机、树莓派或软路由)上跑 ponytail。
- 修改配置
bind = "0.0.0.0:53",让局域网内其他设备可以访问。 - 路由器的 DHCP 服务把 DNS 指向这台设备,或者手动把设备 DNS 指过去。
这样,局域网里所有设备的 DNS 查询都会先到 ponytail,再由它通过 DoH 转发到上游。这就是一种“透明代理”的效果——客户端不知道也不关心 DNS 查询最终是怎么出去的。
但请注意,这种用法对稳定性要求很高。一旦 ponytail 进程挂了,整个局域网的 DNS 解析都会瘫痪(因为你把 DNS 都指向它了)。所以我不太建议用 ponytail 做旁路网关的 DNS 服务,至少得加一层守护进程或者健康检查,比如在前端再挂一个dnsmasq做缓存和高可用,ponytail 只负责“翻译”给 DoH。
5.3 与 sing-box、mosdns、dnsproxy 的横向对比
跑完 ponytail 之后,我又测试了另外几个工具,用来横向对比。这里直接给出我的测试感受:
| 工具 | 定位 | 缓存 | 稳定性 | 配置复杂度 | 适用场景 |
|---|---|---|---|---|---|
| ponytail | DNS shim / DoH proxy | 无 | 实验级 | 简单 | 学习、临时桥接 |
| dnsproxy | DNS 代理(AdGuard 出品) | 有 | 生产级 | 简单 | 本机/局域网 DoH 代理 |
| sing-box DNS 模块 | 代理工具的 DNS 模块 | 有 | 生产级 | 中等 | 与代理工具集成 |
| mosdns | 灵活 DNS 路由/转发 | 有 | 生产级 | 较复杂 | 自定义 DNS 路由策略 |
- dnsproxy:和 ponytail 定位最接近,但它支持缓存、支持更多查询类型、稳定性更好,而且是 AdGuard 出品的,维护活跃。如果你要一个“ponytail 的成熟版”,选它。
- sing-box DNS 模块:sing-box 是现在很流行的代理工具,它的 DNS 模块功能非常强,可以做 DNS 分流、缓存、DoH 上游等。如果你已经在用 sing-box,就没必要再加一个 ponytail。
- mosdns:灵活性最高,支持各种 DNS 路由规则,但配置也最复杂。适合喜欢折腾的人。
5.4 一个可复现的家庭实验拓扑
最后,分享一个我在家里验证过的小拓扑,供你参考。这个拓扑的完整链路是:
应用/设备 ↓ UDP 53(传统 DNS) ponytail (127.0.0.1:53 或 0.0.0.0:53) ↓ HTTPS(DoH,HTTP/2 或 HTTP/3) Cloudflare / Google / AdGuard DoH 上游配置上,我用的是 AdGuard 的 DoH 服务,因为它在国内的可达性比 Cloudflare 更稳定:
bind = "127.0.0.1:53" upstream = "https://dns.adguard.com/dns-query" bootstrap = "https://94.140.14.14/dns-query" probe = "https://dns.adguard.com/dns-query" [fallback] upstream = "https://dns.google/dns-query" bootstrap = "https://8.8.8.8/dns-query" probe = "https://dns.google/dns-query" log = "info"启动后,我用dig连续查询了几个域名,确认响应正常,然后用抓包工具确认 HTTPS 请求确实发出去了。整个过程前后不到十分钟。这个拓扑很适合作为你理解 DoH 代理的第一块试验田。
6. 澄清“ponytail 插件”误读,并给出可直接上手的替代方案
写这篇文章之前,我顺手搜了一下“ponytail”相关的热搜词,发现有不少人在搜“ponytail plugin”“ponytail skill”“插件 ponytail 如何使用”。
这其实是个很有意思的误读:很多人把 ponytail 当成某种插件或 AI 技能——比如 Obsidian 插件、Neovim 插件,或者某个工作流自动化技能。但实际上,ponytail 就是一个独立的 DNS 服务器/代理程序,跟“插件”没有任何关系。
6.1 为什么有人会搜“ponytail plugin / skill”
我猜测,这些搜索大概率是出于以下原因:
- 在某篇文章或视频里看到了“ponytail”这个词,但上下文模糊。
- 把 ponytail 和某个真正叫“ponytail”的插件混淆了。
- 单纯因为“ponytail”这个词听起来像某个轻量级工具的名字,搜索时直接带上了“插件”“skill”等后缀。
不管原因是什么,结论都一样:ponytail 不是插件,不需要安装在 Obsidian、vim 或任何宿主应用里。它是一个命令行程序,用 TOML 配置,跑起来之后就是一台 DNS 服务器。
如果你确实需要的是“DNS 相关的插件/技能”,可以看看下面几个替代方案。它们虽然不是插件,但能直接解决“传统 DNS 转 DoH”这个需求。
6.2 替代方案一:dnsproxy
dnsproxy 是 AdGuard 出品的开源 DNS 代理工具,和 ponytail 定位最接近,但成熟得多。它支持:
- DoH、DoT、DoQ(DNS over QUIC)上游。
- DNS 缓存。
- 多种查询类型(不只是 A/AAAA)。
- 跨平台,Windows/macOS/Linux 都有二进制。
我的使用体验是,dnsproxy 的配置同样不复杂。一个典型用法:
dnsproxy --port 53 --upstream https://cloudflare-dns.com/dns-query --cache一条命令就能在本地 53 端口起一个带缓存的 DoH 代理。比 ponytail 稳定,也比 ponytail 功能全面。如果你在找“成熟版的 ponytail”,选它准没错。
6.3 替代方案二:sing-box DNS 模块
sing-box 是一个通用的代理工具,它的 DNS 模块做得非常强大。如果你已经在用 sing-box 做代理,完全不需要再单跑一个 ponytail——直接在 sing-box 配置里开启 DNS 模块并指定 DoH 上游即可。
sing-box DNS 模块支持:
- 多上游、DNS 分流(按域名规则走不同上游)。
- 缓存、fallback。
- DoH、DoT、UDP 等多种协议。
配置虽然比 ponytail 复杂,但灵活性也高得多。如果你对“DNS 分流”有需求(比如国内域名走国内 DNS、国外域名走 DoH),sing-box 是更好的选择。
6.4 替代方案三:mosdns / systemd-resolved
再补充两个方案。
mosdns是一个高度可定制的 DNS 转发/路由工具。它最强大的地方是支持插件化的 DNS 处理流程,你可以定义复杂的规则链:某些域名直接返回、某些域名走 DoH、某些域名走 UDP 53。如果你喜欢折腾,mosdns 能给你最大的控制力。但它的配置也最复杂,新手可能会觉得劝退。
systemd-resolved是 Linux 系统自带的解析器,很多人可能没注意到它其实支持 DoT 和 DoH。如果你用的是较新的 systemd 版本,可以在/etc/systemd/resolved.conf里直接配置 DoH 上游:
[Resolve] DNS=cloudflare-dns.com#8.8.8.8 DNSOverTLS=yes这个方案的好处是完全系统原生,不需要额外安装工具。但它有一个限制:systemd-resolved 的 DoH 支持在部分版本上不如 DoT 完善,而且配置自由度不高。
综合来看,如果你想要一个“一键解决传统 DNS 转 DoH”的方案,dnsproxy 和 sing-box 是最推荐的。ponytail 则更适合当你学习 DoH 代理原理的入门工具。
我个人的体会是,像 ponytail 这样的小工具,价值不在于它有多稳定、多完善,而在于它把“DNS 垫片”这个概念做成了一个可以拿在手里玩的实物。你跑一次日志、抓一次包、看一眼它怎么把 UDP 53 的查询变成 HTTPS 请求,比读十篇原理文章都管用。
最后分享一个小技巧:如果你也想在本地快速抓包观察 DoH 转换过程,可以用tcpdump监听本地回环接口的 53 端口和 443 端口:
sudo tcpdump -i lo -nn 'port 53 or port 443'然后开一个 ponytail,用dig随便查一个域名,你就能在抓包结果里同时看到 UDP 的 DNS 查询和 HTTPS 的 DNS 查询请求了。那种“原来它是这么干的”的感觉,比任何文档都来得直观。