域名明明没解析,为什么跳转还是生效了?

客户反馈域名跳转不生效,我却一测就是 301。查到最后发现:跳转确实是”生效”了,但生效的原因是个乌龙。

下文域名与 IP 均为示意:old.com 是准备跳转的旧域名,new.com 是新域名,203.0.113.10 表示实际抓到的 Cloudflare 边缘 IP。真实排查时请替换成自己的域名和实测 IP。

现象:客户说跳不动,我这边却一跳一个准

客户在 Cloudflare 上给旧域名 old.com 配了 301 跳转,目标是新域名 new.com。客户反馈:在国内访问 old.com,页面根本不会跳转。

我这边连上代理一测,curl 稳稳返回 301。

第一个怀疑是”屏蔽了国内访问”——是不是旧域名上有针对中国大陆的访问规则?检查后发现确实有类似配置,于是先把它删掉,再让客户试,依然不行

问题就变成了:为什么我开代理能跳,客户在国内不能跳?

排查:先看 DNS,而不是急着看规则

跳转要生效,前提是访问者能先”连上 Cloudflare”。如果域名解析都失败,规则写得再好也没机会执行。于是先查 old.com 的解析:


dig @1.1.1.1 old.com. A
dig @1.1.1.1 www.old.com. A
dig @1.1.1.1 old.com. CNAME

结果很干净:根域名 @www 的 A、AAAA、CNAME 一条都没有。也就是说,这个域名在公网上根本没有可用地址。顺手用 httpstatus.io、redirect-checker.org 测,结果同样测不出跳转——它们也解析不到这个域名。

到这里矛盾出现了:解析都不存在,理论上任何人都不该跳转成功,可我的代理就是能拿到 301。

用 curl 复现:同一套规则,两种结果

先确认代理上的 301 是真的,而不是浏览器缓存。用 curl 只看响应头,并把连接 IP、状态码、跳转目标一起打出来:


curl -sS -D - -o /dev/null \
  --connect-timeout 10 --max-time 20 \
  -w '\n连接 IP: %{remote_ip}\n状态码: %{http_code}\n跳转目标: %{redirect_url}\n' \
  'https://old.com/'

输出大致是这样(IP 是示意,实际值来自上面 curl 的 remote_ip):


HTTP/1.1 301 Moved Permanently
Location: https://new.com/
Server: cloudflare
CF-RAY: 8f5ea2...-NRT

连接 IP: 203.0.113.10
状态码: 301
跳转目标: https://new.com/

关键信息是 %{remote_ip}——它告诉我们 curl 真正连到了哪个 Cloudflare 边缘 IP,后面要用它做对照实验。

接着在国内测试机上复现失败场景。Windows 下用 curl.exe(避开 PowerShell 的别名),先关掉代理软件的 TUN/全局代理,不指定任何 IP 直接请求:


curl.exe --noproxy "*" --connect-timeout 10 --max-time 20 -v -o NUL \
  -w "\nIP: %{remote_ip}\nHTTP: %{http_code}\n" "https://old.com/"

结果连 DNS 阶段都没过去:


* Could not resolve host: old.com
curl: (6) Could not resolve host: old.com

同样的域名、同样的规则,这次用 --resolveold.com 强行指到刚才那个 Cloudflare 边缘 IP 再试。注意 --resolve 只改连接地址,URL、TLS 的 SNI 和 HTTP 的 Host 仍然是 old.com


curl.exe --noproxy "*" --resolve "old.com:443:203.0.113.10" \
  --connect-timeout 10 --max-time 20 -v -o NUL \
  -w "\nIP: %{remote_ip}\nHTTP: %{http_code}\n" "https://old.com/"

这次就正常返回了 301,Location 指向 new.com

这个对照组把问题圈死了:

  • Cloudflare 的跳转规则一直是好的,谁的请求只要带着 old.com 到达边缘节点,都会返回 301,没有针对国内网络的区别对待;
  • 国内访问失败不是 IP 被墙——同一个边缘 IP 让客户直接 ping 也能通;
  • 唯一的差别是解析:国内机器解析 old.com 失败,连 Cloudflare 都到不了,自然谈不上跳转。

可这样一来新的矛盾又出现了:代理机器的 DNS 配置看着也正常,它凭什么能解析到那个 IP?

转折:ping 的时候,域名自己多了一个后缀

在代理服务器上 ping 旧域名,注意看输出:


$ ping old.com
PING old.com.com (203.0.113.10) 56(84) bytes of data.

它解析的根本不是 old.com,而是 old.com.com——域名后面被偷偷追加了一个 .com。而且 old.com.com 恰好也是 Cloudflare 的服务,返回的正是之前 curl 连上的那个 IP。

curl 拿到这个 IP 去建连时,请求里带的域名仍然是 old.com(SNI 和 Host 都没变)。Cloudflare 收到对 old.com 的请求,一看规则存在,直接回了 301。

所以跳转”生效”了——生效得莫名其妙。

根因:glibc 拿 hostname 猜了一个搜索域

为什么代理服务器会去解析 old.com.com?看它的 DNS 配置:


$ cat /etc/resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4

只有 nameserver,既没有 search 也没有 domain。这种情况下,Linux 的 glibc 解析器会自己做主:当没有配置搜索列表时,从本机 hostname 第一个点之后的部分推导默认搜索域

代理服务器的主机名大概是这个画风:


$ hostname
vps.com

于是默认搜索域被推导成 .com。解析 old.com 失败后,解析器继续尝试 old.com 拼接上 .com,也就是 old.com.com——这个域名有解析,还指向 Cloudflare。整条链路就这么通了。

用这个机制回头看,所有现象都说得通:

  • 我(代理)能跳:不是 Cloudflare 在兜底,是代理机器的 DNS 走了条”邪路”,碰巧撞上一个能用的 IP;
  • 客户在国内不能跳:国内网络老老实实解析 old.com,解析失败就是连不上,自然拿不到 301;
  • 跟”屏蔽国内”没关系:规则删了也一样,真正的瓶颈是旧域名压根没有解析。

验证与修复

想确认是不是搜索域在捣鬼,对比带不带尾点(尾点表示绝对域名,不追加搜索后缀)的解析结果:


getent ahostsv4 old.com     # 可能能解析出来:被追加了后缀
getent ahostsv4 old.com.    # 解析失败:不带后缀才是真实情况

也可以顺着 hostname 把来源查清楚:


hostname
cat /etc/hostname
cat /etc/resolv.conf

修复方向很简单:让旧域名在公网上真正可解析。如果它只用来做跳转,可以按 Cloudflare 官方做法,给 @www 各加一条 A 记录 192.0.2.1 并开启代理(橙云)。这样访问者能正常解析到 Cloudflare 边缘节点,跳转规则才有机会执行。

总结

这次排查给我留下几个印象:

  • “我这边能通”不等于”配置没问题”。本地解析路径可能骗人,你连上的未必是你以为的那个服务器。
  • 排查跳转问题先查 DNS。解析都失败了,就别急着怀疑规则写错、网络被墙。
  • curl 的 -w 参数记得用%{remote_ip}%{http_code}%{redirect_url} 一次打全,能省很多来回确认的功夫。
  • 看到”能通但不能通”的怪现象,多用 diggetentping 看实际解析结果,域名输出和输入对不上,往往是关键线索。
发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

或许还会想看: