IPv6 用户打不开站点:AAAA 记录缺失或挂了 —— 完整排查

IPv4 用户正常,IPv6 用户全部超时。要么 AAAA 没配、要么指向死地址、要么源站防火墙挡 v6。双栈连通性排查。

一句话结论: IPv4 正常、IPv6 用户全超时,最快的修法几乎永远是把坏掉的 AAAA 记录删掉(坏 AAAA 比没 AAAA 更糟——“没 AAAA”时 Happy Eyeballs 会干净回落,“坏 AAAA”则会浪费时间)。先用 curl -6 -I https://yourdomain.com(卡住)对比 curl -4 -I https://yourdomain.com(正常)确认故障,然后要么删掉过期 AAAA、要么把源站的 IPv6 路径修通。详见下文。

某个用户从运营商移动网(移动 / 联通 / Verizon Wireless / T-Mobile / Vodafone……)或现代家庭宽带反馈站点打不开——TCP 层就超时。你在公司有线上一开秒开。差别就在 IPv6:移动运营商和很多 APAC 运营商提供 IPv6 优先 / 仅 IPv6 接入,浏览器的 Happy Eyeballs 算法先试 AAAA。你的 AAAA 指向死主机、或在 CDN 前置场景下根本不该存在、或源站防火墙丢 IPv6 包——用户就要等 TCP 完整超时(一般 75-120 秒),可能才回落到 IPv4,那时候 tab 早关了。这个故障对大多数监控不可见,因为 IPv4 健康检查全过。

一个容易让人意外的细节:AAAA “没有”是没问题的,“坏掉”才是最差情形。在 Happy Eyeballs v2(RFC 8305)下,客户端同时拿到 A 和 AAAA 时会先发起 IPv6 连接,如果在 Connection Attempt Delay(截至 2026 年 6 月推荐值为 250 毫秒)内还没连上,才并发去试 IPv4。要是压根没有 AAAA,客户端根本不会在 v6 上浪费时间。所以”删掉坏记录”是真的能消除症状的,“补一条半通的记录”则不能。

你属于哪一类?

先跑这两条命令,再看表。

dig AAAA yourdomain.com +short      # 到底有没有 AAAA?
curl -6 -I https://yourdomain.com   # v6 真能打到你吗?
dig AAAA 返回curl -6 结果最可能的原因跳到
什么都没有(不适用)根本没 AAAA——没问题,除非移动用户还在抱怨(见 NAT64)原因 6
一个地址connection refused / TCP 卡住源站防火墙或代理没监听 v6原因 3 / 4
一个地址打开的是别的站 / 证书不匹配AAAA 绕过 CDN 直指裸源站原因 2
一个地址超时,且 A 指向别处旧主机留下的过期 AAAA原因 1
一个地址能通,但用户还说慢旧的坏 AAAA 仍被高 TTL 缓存住原因 5

常见原因

按用户可观察到的疼痛排序。

1. AAAA 存在但指向不再托管你的 IP

你以前主机同时有 A 和 AAAA。换到新主机(Vercel / Netlify / 别的 VPS),A 更新了 AAAA 忘了。旧 IPv6 地址要么是死的、要么现在是别人的。

怎么判断dig AAAA yourdomain.com +short 返回一个地址,但 curl -6 -I https://yourdomain.com 超时或返回错误内容。

2. CDN 前置只送 IPv4,AAAA 直接绕过 CDN

你的 CDN(老的 Cloudflare 配置、某些 Bunny 设置、自定义边缘)IP 列表只暴露 IPv4,但你手动加了一条 AAAA 指向源站”帮 IPv6 用户”。那些用户直接绕过 CDN,命中没配好 / 不可达的源站。

怎么判断:A 记录是 CDN IP,AAAA 是源站 IP。curl -6 直击源站,没有 CDN 保护。

3. 源站防火墙丢 IPv6 包

ip6tables(或云防火墙 / 安全组)没配。默认拒绝阻挡 :443 入站 v6。源站技术上能双栈,但 v6 没人能进。

怎么判断sudo ip6tables -L INPUT -v -n 看不到 :443 v6 的 ACCEPT 规则。外部:nc -6 -zv yourdomain.com 443 卡住。

4. 反向代理 / nginx 不监听 v6

OS 防火墙开了,nginx 默认 listen 443 ssl; 只是 IPv4。需要显式 listen [::]:443 ssl;

怎么判断ss -tlnp | grep :443 显示 0.0.0.0:443 没有 [::]:443。或者都显示但只绑了 IPv4。

5. Happy Eyeballs 会 fallback,但 AAAA TTL 太长缓存住了

哪怕你把坏 AAAA 删了,浏览器还会按 TTL 缓存(常见 4-24 小时)。用户每次还是先打 v6、超时、回落 v4——好几个小时都”慢”。

怎么判断:用户说”网最后能开,但每次要 30 秒”。浏览器 DevTools 时间线里 DNS / 连接阶段超长。

6. 运营商 NAT64 / CLAT 引入 SNI 问题

移动运营商用 NAT64(终端没原生 v4),从你的 A 合成 AAAA。SNI / Host header 逻辑要是依赖源地址族就崩。

怎么判断:A 正常、你没 AAAA,但运营商用户还是抱怨。SNI 拿到的是 well-known-ipv6-pref 合成地址。按运营商具体看。

开始前

  • 看清楚哪些用户 / 地区受影响——移动 vs 家庭宽带 vs 公司网行为差很多。
  • 确认托管平台的 IPv6 态度:暴露 AAAA 吗?Cloudflare 默认是,纯 VPS 看配置。
  • 找一个能用 IPv6 的客户端测。test-ipv6.com 能确认客户端 v6 是好的。
  • 知道源站的 IPv6 地址(云厂商控制台能看)。

需要收集的信息

  • dig A yourdomain.com +shortdig AAAA yourdomain.com +short 输出。
  • curl -4 -I https://yourdomain.com curl -6 -I https://yourdomain.com 输出。
  • 源站 ss -tlnp | grep :443 输出。
  • 源站 ip -6 addr show 输出(看分配的 v6 地址)。
  • 从一个 IPv6-only / IPv6-preferred 网络的测试——手机流量是最容易的来源。

一步步修

顺序:尽快把坏 AAAA 干掉,再做好双栈。

第 1 步:从外部测两栈

curl -4 -v https://yourdomain.com 2>&1 | head -20
curl -6 -v https://yourdomain.com 2>&1 | head -20

-4 成功、-6 卡或挂,诊断确认。注意 -6 在哪一层挂:TCP connect、TLS 握手、HTTP 响应——每层意味着不同子问题。

第 2 步:删掉死 AAAA

dig AAAA yourdomain.com 返回的地址确实不应答:

  1. DNS 面板 → 找到 AAAA 记录。
  2. 删了(或改成对的地址)。
  3. 把附近记录的 TTL 临时降到 300 秒,方便后续改动快速传播。

浏览器和解析器会在旧 TTL 内开始忽略你的域名的 v6,Happy Eyeballs 干净回落 v4。

第 3 步:双栈源站确认服务器监听 v6

编辑 /etc/nginx/sites-available/yourdomain

server {
    listen 443 ssl;
    listen [::]:443 ssl ipv6only=on;
    http2 on;
    server_name yourdomain.com;
    # ...
}

[::]:443 这一行绑 IPv6。现代 nginx 有两处变化常把人绊倒:

  • 从 nginx 1.25.1 起,http2 是独立指令(http2 on;),不再是 listen 上的标志。多数构建两种写法都能用,但截至 2026 年 6 月,独立指令才是当前写法。
  • ipv6only=onlisten [::] socket 的默认值(自 nginx 1.3.4 起),而且 nginx 会显式设置 IPV6_V6ONLY socket 选项,覆盖内核的 net.ipv6.bindv6only sysctl。也就是说 [::]:443 只收 IPv6——你确实需要单独那一行 listen 443 ssl; 来接 IPv4。别以为一个 socket 把两个协议族都覆盖了。

重启:

sudo nginx -t && sudo systemctl reload nginx
ss -tlnp | grep :443

输出现在应该同时有 0.0.0.0:443:::443

第 4 步:开 IPv6 防火墙

sudo ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo ip6tables-save | sudo tee /etc/ip6tables.rules

ufw

sudo ufw allow 443/tcp
sudo ufw allow 80/tcp

ufw 默认同时对 v4 v6 生效。验证:

sudo ip6tables -L INPUT -v -n | grep 443

云厂商控制台(AWS 安全组 / GCP 防火墙 / DigitalOcean Cloud Firewall)里,明确把 IPv6 来源 ::/0 加给 80/443——很多 UI 默认只是 0.0.0.0/0

第 5 步:加上正确的 AAAA 记录

确认源站支持 v6 后,取地址:

ip -6 addr show | grep "scope global"

挑非临时、非链路本地的(一般是没有 temporary 标志的那个)。在 DNS 里加 AAAA,TTL 先 300:

yourdomain.com.  300  IN  AAAA  2001:db8::1

测:

dig AAAA yourdomain.com @1.1.1.1 +short
curl -6 -I https://yourdomain.com

第 6 步:用 CDN 的话,优先用 CDN 提供的 IPv6,不要自己加

Cloudflare 代理(橙云)记录会自动同时发 A 和 AAAA,都指向 Cloudflare 边缘 anycast 地址。已代理的记录绝对不要手动加一条绕过 Cloudflare 的 AAAA——那样 IPv6 客户端会整个绕过边缘。

这个开关在 Cloudflare 控制台 → 你的域名 → Network → IPv6 Compatibility(默认开;该开关仅在 Enterprise 套餐可编辑,否则一直是开着的)。没有特殊理由就别关。另外,对于源站同时有 IPv4 和 IPv6 地址的已代理记录,Cloudflare 回源时优先走 IPv4,所以边缘侧的 IPv6 能用,并不严格要求源站自己 v6 是通的。

Vercel / Netlify 在你使用它们的 nameserver 或推荐的 CNAME/ALIAS 目标时会替你托管记录——别在上面再手动加 AAAA,否则会造成劈裂:v6 客户端打到的端点和 v4 客户端不一样。加任何手动记录前先查当前平台文档。

验证

  • dig AAAA yourdomain.com +short 返回正确地址(或什么都没有,故意只 v4 的话)。
  • curl -6 -I https://yourdomain.com 返回 HTTP/2 200 或预期响应,不超时。
  • test-ipv6.com(从某个网络)显示站点 v6 可达。
  • 从手机流量(移动数据热点给笔记本)开网页无延迟。
  • 源站 ss -tlnp 显示 443 同时有 v4 和 v6 监听。

长期预防

  • 监控加 IPv6 健康检查(UptimeRobot / Pingdom / Better Stack 都支持 v6 探测——要显式打开)。
  • 迁移主机时 A 和 AAAA 同步更新,不要留旧 AAAA 指向旧 VPS。
  • 标准化 nginx / haproxy / caddy 模板,开局就带 [::]:443
  • 每月用移动网测一次——大多数 IPv6 故障只在那看得见。
  • runbook 里写:每个对外服务,要么有可用的 AAAA、要么没 AAAA,绝不留一个坏的。

常见坑

  • 死 AAAA 留着不管,因为”v4 还能用”——你正在静默劣化 v6 优先用户的体验。
  • AAAA 加了忘开 v6 防火墙——记录指向丢包的主机。
  • 哪里都用 0.0.0.0/0 以为 v6 也覆盖——大部分防火墙 v4 和 v6 是两套规则。
  • AAAA TTL 设 86400 然后想修错误——用户一整天都拿着坏记录。
  • 云厂商自动分配临时 v6(隐私地址)加到 DNS——重启就变。

FAQ

Q:2026 年了我是不是必须支持 IPv6,即使用户主要是桌面?

是。移动流量经常 IPv6 占多数,即使桌面不是。坏 AAAA 主动伤害用户;没 AAAA 反而能接受——因为 Happy Eyeballs v2(RFC 8305)在 v6 连不上时,会在 250 毫秒的 Connection Attempt Delay 后回落到 IPv4。

Q:我直接全删 AAAA、忘掉 IPv6 行不行?

行,是个有效立场。完全没 AAAA 时,每个客户端都直接走 IPv4,没有任何代价。坏的失败模式是 AAAA 是错的,不是没 AAAA。正在救火时,删掉 AAAA 是最稳的应急动作。

Q:删掉 AAAA 后,用户要多久才不再打那个死 IPv6 地址?

最多等到记录的 TTL。解析器和浏览器会按这个时长缓存旧 AAAA(常见 4-24 小时)。改之前先把 TTL 降到 300 秒,清理就能在五分钟内传播开;如果你正在事故中、而旧 TTL 又很高,没法强制让别人的缓存过期——只能等(或者临时把 v6 端点修通,让被缓存的那条记录能用)。

Q:我源站在 AWS——默认有 IPv6 吗?

没有。你得给 VPC 关联一个 IPv6 CIDR、给子网分一个 /64,再设 assign_ipv6_address_on_creation = true 或手动给实例 / ENI 挂地址。老的 legacy VPC 在你迁移之前都是仅 IPv4。注意普通子网无法关掉 IPv4,但你可以建仅 IPv6 子网(--ipv6-native),想要 v6 优先的实例就用它。去控制台查 VPC 和子网设置。

Q:Cloudflare 橙云自动给我 IPv6 吗?

是——代理记录无论源站什么栈,都同时发 Cloudflare 的 IPv4 和 IPv6 anycast 地址,由 Network → IPv6 Compatibility 开关控制(默认开)。这是不动源站就支持 IPv6 最省事的方式,而且 Cloudflare 回源仍走 IPv4。

参见 A 记录跟 CNAME 分不清DNS 改了站点还是打不开子域名不解析

外部参考:回落时序出自 RFC 8305: Happy Eyeballs v2,Cloudflare 自动 AAAA 的行为见 IPv6 compatibility。测自己客户端先上 test-ipv6.com

标签: #排查 #DNS #ipv6 #排查 #Networking