前面三篇文章帮你搞清楚了 IPv6 是什么、地址怎么工作、和 IPv4 有什么区别——那些是”知道”。这一篇只讲”做到”:怎么检测一个网站是否支持 IPv6、怎么排查 DNS 配置、怎么在路由器上配端口转发、以及我在运维中遇到过的几个经典故障和修复方法。

不讲理论,只讲命令和排错步骤。

IPv6 网站连通性检测流程 ① DNS 检测 dig AAAA 目标域名 AAAA? 目标不支持 IPv6 ② 连接检测 curl -6 连通? ③ 服务端配置 ✅ IPv6 可用 AAAA 不存在 → 目标不支持 IPv6,停 curl -6 超时 → 有 AAAA 但端口不通 检查服务端 listen 配置 & 防火墙
图:IPv6 连通性检测三层流程 — DNS → 连接 → 服务端

DNS 层检测:dig AAAA 与 nslookup

一个网站是否支持 IPv6,最直接的方法看它的 DNS 记录——有没有 AAAA 记录。AAAA 记录是 IPv6 地址在 DNS 里的映射,等效于 IPv4 的 A 记录。

# dig 查 AAAA 记录 —— Linux/Mac 自带,Windows 需要安装
dig AAAA google.com

# 只显示答案部分
dig AAAA google.com +short
# 返回:2a00:1450:4001:811::200e

如果返回了一个以 23 开头的 IPv6 地址(这是 Global Unicast 的范围),说明目标网站有 IPv6 DNS 记录。如果 +short 什么都不返回,说明目标没有 AAAA 记录——你的 IPv6 代理再好也访问不了这个网站。

在 Windows 上可以用 nslookup:

nslookup -type=AAAA google.com

也可以用在线工具 test-ipv6.com,它会自动检测你当前的网络是否支持 IPv6,不需要手动输命令。我通常用它来给非技术同事做一个快速检查——打开网页等几秒,结果就出来了。

踩坑提醒:AAAA 记录存在 ≠ 网站可以通过 IPv6 访问。DNS 只告诉你域名解析到了哪个 IPv6 地址,不代表目标服务器的 80/443 端口正在监听 IPv6。你需要做连接层检测才能确认。很多新手查了 AAAA 有返回就以为没问题,结果 curl -6 的时候 timeout——因为目标在 DNS 里写了 AAAA 但 Web 服务器没绑 IPv6。

核心观点:dig AAAA 是第一道关,过了只说明 DNS 层面支持 IPv6。连接层和传输层还要做进一步检测。

连接层检测:curl -6 与浏览器工具

DNS 检查通过了,下一步是实际建立 IPv6 连接。我用 curl -6 做这一步——它强制 curl 使用 IPv6 解析,如果目标不支持会直接报错:

# 强制使用 IPv6 访问目标网站
curl -6 -I https://google.com

# 成功返回 HTTP 头
# HTTP/2 200
# ...

# 如果不支持 IPv6,会报:
# curl: (7) Failed to connect to google.com port 443: Network is unreachable

-I 只取响应头,不发 GET 请求,轻量快速。如果想看完整的连接过程,加 -v

curl -6 -v https://google.com 2>&1 | grep -E "Connected|SSL|HTTP"

浏览器里也有对应的检查方法:打开 Chrome 开发者工具 → Network 面板 → 找一个请求 → 看 Remote Address 字段。如果显示 [2a00:1450:...] 这样的 IPv6 地址,说明这个连接走的是 IPv6。

核心观点:curl -6 是检测 IPv6 连接最快速的方法,加 -I 只拿响应头最省资源。网站支持 IPv6 的判断标准是——DNS 有 AAAA + TCP 443 端口实际能连通 + 返回了正常的 HTTP 响应。

服务端检测:Nginx/Apache 监听 IPv6

如果你在管理 Web 服务器,确认它是否正确监听 IPv6:

# Nginx 配置 —— listen 后加 ipv6only=on 或直接监听 [::]
server {
    listen [::]:80;
    listen [::]:443 ssl http2;
    server_name example.com;
    ...
}

[::]:80 表示同时监听 IPv4 和 IPv6 的 80 端口(在大多数系统上是双栈行为)。如果只想监听 IPv6,加 ipv6only=on

# Apache —— 在 VirtualHost 里加 IPv6 地址
<VirtualHost *:80 [2001:db8::1]:80>
    ServerName example.com
    ...
</VirtualHost>

配置完后用 netstatss 确认监听状态:

# 检查 80 端口是否在 IPv6 上监听
ss -tlnp | grep :80
# 如果输出中有 [::]:80 或具体的 IPv6 地址:80,说明配上了

核心观点:Nginx 用 [::] 是最简洁的方式——一个 listen 指令同时支持双栈。Apache 需要在 VirtualHost 里显式写 IPv6 地址。

端口转发配置

IPv6 端口转发的逻辑跟 IPv4 完全不同。IPv4 上你做的是 NAT 端口映射(把公网 IP 的某个端口映射到内网某台设备的某个端口),IPv6 上你做的其实不是”转发”,而是”防火墙放行”——因为 IPv6 设备本身就有公网地址,不存在内网地址到公网地址的转换。

你只需要在路由器的防火墙规则里允许外部对某个端口发起连接:

OpenWrt / Linux iptables

# 允许外部访问内网设备 2001:db8:1::100 的 443 端口
ip6tables -A FORWARD -p tcp --dport 443 -d 2001:db8:1::100 -j ACCEPT

注意用的是 ip6tables 而不是 iptables——iptables 管 IPv4 流量,ip6tables 管 IPv6 流量。很多人在这里配错了规则然后发现不起作用。

pfSense / OPNsense

在 Firewall → Rules → WAN 里加一条规则:Protocol 选 TCP,Destination 填目标设备的 IPv6 地址,Destination Port 填你要开放的端口,Action 选 Pass。

普通家用路由器

大部分家用路由器的端口转发界面上有一个”协议类型”的选择——选 TCP 而不是 Both(避免不必要的 UDP 暴露)。目标 IP 填你内网设备的 IPv6 地址。如果路由器的界面只支持 IPv4 地址输入(4 段数字),说明你的路由器固件太老,不支持 IPv6 端口管理,需要刷 OpenWrt 或换设备。

踩坑提醒:运营商分配的 IPv6 前缀是动态变化的(除非你申请了固定前缀)。路由器重启后前缀变掉,你配的端口转发规则里写的目标 IPv6 地址就失效了。解决方案是让目标设备使用固定后缀(比如用 EUI-64 或手动指定接口 ID),然后在规则里只匹配后缀部分,前缀变化时规则仍然有效。

核心观点:IPv6 下端口转发本质是防火墙放行,不是地址转换。关键是确保目标设备的 IPv6 地址在运营商前缀变化时不会失效。

防火墙与 ICMPv6 常见故障

IPv4 时代很多人习惯把 ICMP 全部禁掉——觉得 ping 不通更安全。但在 IPv6 下这会造成严重问题,因为 ICMPv6 不只是用来 ping 的——它承载了邻居发现(NDP)、路径 MTU 发现(PMTUD)等关键功能。

如果禁掉了 ICMPv6,以下功能会受影响:

  • 邻居发现失败——设备无法通过 NDP 学习同一链路上其他设备的 MAC 地址,等于局域网都不通
  • PMTUD 失效——路径 MTU 发现依赖 ICMPv6 Packet Too Big 消息,禁掉后遇到 MTU 不匹配的链路连接会 hang 住
  • DAD 异常——重复地址检测使用 ICMPv6,禁掉后可能出现 IP 地址冲突

正确的做法是只放行必要的 ICMPv6 类型,而不是一条规则全禁:

# 只放行必要的 ICMPv6 类型
ip6tables -A INPUT -p icmpv6 --icmpv6-type 1 -j ACCEPT   # Destination Unreachable
ip6tables -A INPUT -p icmpv6 --icmpv6-type 2 -j ACCEPT   # Packet Too Big (PMTUD)
ip6tables -A INPUT -p icmpv6 --icmpv6-type 3 -j ACCEPT   # Time Exceeded
ip6tables -A INPUT -p icmpv6 --icmpv6-type 4 -j ACCEPT   # Parameter Problem
ip6tables -A INPUT -p icmpv6 --icmpv6-type 128 -j ACCEPT # Echo Request (ping)
ip6tables -A INPUT -p icmpv6 --icmpv6-type 129 -j ACCEPT # Echo Reply
ip6tables -A INPUT -p icmpv6 --icmpv6-type 133 -j ACCEPT # Router Solicitation (NDP)
ip6tables -A INPUT -p icmpv6 --icmpv6-type 134 -j ACCEPT # Router Advertisement (NDP)
ip6tables -A INPUT -p icmpv6 --icmpv6-type 135 -j ACCEPT # Neighbor Solicitation (NDP)
ip6tables -A INPUT -p icmpv6 --icmpv6-type 136 -j ACCEPT # Neighbor Advertisement (NDP)
踩坑提醒:防火墙放行了 TCP 443 但没放 ICMPv6 的 Packet Too Big(类型 2),这是 IPv6 环境里最隐蔽的故障。表面上 TCP 握手成功了,但大文件传输中途卡死——因为中间的某个链路 MTU 小于 1500,而 PMTUD 消息被防火墙挡了,发送方不知道要调整包大小,连接就 hang 住了。排查这种问题要抓包才能发现,非常浪费时间。

核心观点:IPv4 禁 ICMP 可以凑合,IPv6 禁了 ICMPv6 直接断网。NDP 和 PMTUD 依赖 ICMPv6,不是可选的。

三个经典故障的排查过程

故障一:AAAA 记录有,但 curl -6 超时

现象:dig AAAA example.com 有返回,但 curl -6 https://example.com 几秒后 timeout。

排查步骤:

  1. 先确认你自己的 IPv6 是通的:ping -6 google.com。如果不通,问题在你这边。
  2. curl -6 -v https://example.com 看卡在哪一步——如果卡在 TCP 握手,说明目标 Web 服务器没监听 IPv6 的 443 端口。
  3. 用在线端口扫描工具测目标的 443 端口在 IPv6 上是否开放。如果 DNS 有 AAAA 但 443 不通,说明目标配了 AAAA 但没配 Web 服务器。

这种情况在中小网站里非常常见——运维加了一条 AAAA 记录就以为 IPv6 搞定了,结果 Web 服务器配置里没加 listen [::]:443

故障二:时通时不通,同一个 URL 偶尔能访问偶尔 timeout

现象:采集脚本跑得好好的,突然某些请求 timeout,过几分钟自己又好了。

原因:目标网站的前端用了 CDN 或多层负载均衡,部分节点支持 IPv6、部分不支持。浏览器和 curl 有 Happy Eyeballs 算法(同时尝试 IPv4 和 IPv6,哪个快用哪个),但原始 TCP 连接没有这个机制。

修复:在采集脚本里加 fallback 逻辑——IPv6 连接超时超过 3 秒自动切换到 IPv4。或者直接用双栈 Happy Eyeballs 实现(Python 可以配 aiohttphappy_eyeballs_delay 参数)。

故障三:内网设备可以访问 IPv6 外网,但外网无法访问内网设备

现象:内网设备可以 ping -6 google.com,但从外网 ping -6 你的设备超时。

原因(按概率排序):

  1. 路由器防火墙默认拦截所有入站 IPv6 连接(最常见)
  2. ICMPv6 Echo Request(类型 128)被防火墙挡了
  3. 运营商做了入站过滤(部分 ISP 限制家庭宽带的入站连接)

修复:先检查路由器防火墙规则放行了入站流量到目标端口;再用在线端口扫描工具验证;如果 ISP 做了入站限制,换商业宽带或 VPS 中转。

核心观点:排查 IPv6 故障的黄金法则是逐层检查——先看自己网络有没有 IPv6(ping 外部 IPv6 地址)→ 再看 DNS 有没有 AAAA → 再看 TCP 端口通不通 → 再看应用层。跳过任何一层都可能误判原因。

延伸阅读

检测和配置都搞定了。如果你的业务涉及数据采集或跨境网络——IPv6 代理可能是下一步最值得看的内容。