问题
如果你在家里自托管服务,大概率遇到过这个令人抓狂的问题:你的服务从外部访问完全正常,但当你在家里,用域名去访问的时候,却怎么都不通。
这就是所谓的Hairpin NAT问题(也叫NAT环回),下面是我最终解决它的方法。
什么是Hairpin NAT?
当你从外部访问humfurie.org时,请求会去到你的公网IP,再被转发到内网服务器,一切正常。但当你在自己的网络内部尝试同样的操作时,你的路由器就会搞混,流量走出去,又想转回来,结果就出问题了。
"正规"的解决方法是在路由器上配置hairpin NAT规则。但事实证明这并不可靠,尤其是在MikroTik路由器上——一个叫fasttrack的功能会为已建立的连接绕过NAT规则,导致就算配置正确,hairpin NAT也会悄无声息地失效。
更好嘅解决方案:用AdGuard Home做DNS覆写
与其和NAT规则死磕,更干净的方案是让你的本地网络直接把域名解析到服务器的内网IP。这样就完全不需要hairpin了。
第一步:安装AdGuard Home
在你的Ubuntu服务器上,创建一个docker-compose.yml:
services:
adguardhome:
image: adguard/adguardhome
container_name: adguardhome
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "3000:3000/tcp"
volumes:
- ./adguard/work:/opt/adguardhome/work
- ./adguard/conf:/opt/adguardhome/conf
labels:
- "traefik.enable=true"
- "traefik.http.routers.adguard.rule=Host(`adguard.yourdomain.com`)"
- "traefik.http.routers.adguard.entrypoints=websecure"
- "traefik.http.services.adguard.loadbalancer.server.port=80"
第二步:释放53端口
Ubuntu默认在53端口上使用systemd-resolved。先把它禁用:
sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
然后启动AdGuard:
docker compose up -d
第三步:完成AdGuard设置
访问http://your-server-ip:3000,完成设置向导。设置完成后,把Traefik的标签更新为使用80端口而不是3000端口,然后重启容器。
第四步:把MikroTik的DHCP指向AdGuard
在MikroTik中进入IP、DHCP Server、Networks,把DNS Server设置为你Ubuntu服务器的IP(例如192.168.88.109)。这样网络上所有设备就会自动使用AdGuard。
第五步:在AdGuard中添加DNS重写规则
进入Filters、DNS Rewrites,为每个域名添加条目——你需要同时添加根域名和子域名通配符:
| 域名 | 解析结果 |
|---|---|
| humfurie.org | 192.168.88.109 |
| *.humfurie.org | 192.168.88.109 |
| yourdomain.com | 192.168.88.109 |
| *.yourdomain.com | 192.168.88.109 |
注意: *.humfurie.org只覆盖子域名,并不覆盖根域名本身,所以两条都需要添加。
第六步:验证
更新你的DHCP租约,并测试:
nslookup humfurie.org
响应中应该显示你的内网IP,而不是公网IP。
需要留意的坑
Tailscale: 如果你在用Tailscale,它会覆盖你设备上的DNS。请在Tailscale的设置中关闭"Use Tailscale DNS settings",否则你的设备会完全无视AdGuard。
MikroTik的DNS缓存: MikroTik会缓存DNS响应。如果改完之后还是不生效,在终端里清空缓存:/ip dns cache flush
Docker和53端口: AdGuard的管理界面在初始设置期间跑在3000端口,之后会切换到80端口。设置完成后要相应更新你的Traefik配置。
结果
现在,所有自托管服务无论从网络内部还是外部访问,都能用同一个域名正常打开。作为附赠,AdGuard Home还给你带来了全网范围的广告拦截和DNS查询日志功能。