TECH / SERVER · 2026/04/01 · 6 分钟阅读
用 Cloudflare 搭了个 DDNS,终于解决 IPv6 动态域名问题
家宽给的 IPv6 前缀隔几天就换一次,域名解析跟不上,家里的服务就时好时坏。这篇把整个折腾路径写清楚,让下一次不必重新在雾里摸索。
问题是什么
家里跑了几个自用服务,想通过域名从外面访问。运营商给的是动态 IPv6:地址会变,公网可达,但没有固定性。每次前缀更换之后,域名还指着旧地址,服务就"失联"了。
这正是 DDNS(动态 DNS)要解决的事:让一个小脚本周期性地检测本机当前地址,一旦发现变化,就调用 DNS 服务商的接口把记录更新过去。
为什么选 Cloudflare
- 域名本来就托管在 Cloudflare,API 免费且文档清楚。
- 支持 AAAA 记录,TTL 可以压到很短(如 60 秒),地址切换后很快生效。
- API Token 可以按"仅限编辑某个 Zone 的 DNS"来授权,泄露风险可控。
准备工作
- 在 Cloudflare 后台创建一个 API Token,权限只给
Zone → DNS → Edit,并且限制到目标域名所在的 Zone。 - 记下 Zone ID(域名概览页右下角)。
- 先手动建一条 AAAA 记录,比如
home.taozhiruo.com,内容随便填一个旧地址,待会儿由脚本接管。
核心脚本
整个逻辑只有三步:取本机当前 IPv6 → 和 DNS 上的记录比对 → 不一致就更新。一个 shell 脚本就够了:
#!/usr/bin/env bash
set -euo pipefail
TOKEN="你的_API_TOKEN"
ZONE_ID="你的_ZONE_ID"
RECORD_NAME="home.taozhiruo.com"
# 取一个全局单播的 IPv6(排除临时隐私地址可按需调整)
CURRENT=$(ip -6 addr show scope global \
| grep -oP '(?<=inet6 )[0-9a-f:]+' | head -n1)
API="https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records"
# 查现有记录
REC=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
"${API}?type=AAAA&name=${RECORD_NAME}")
REC_ID=$(echo "$REC" | jq -r '.result[0].id')
REC_IP=$(echo "$REC" | jq -r '.result[0].content')
if [ "$CURRENT" != "$REC_IP" ]; then
curl -s -X PUT -H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
"${API}/${REC_ID}" \
--data "{\"type\":\"AAAA\",\"name\":\"${RECORD_NAME}\",\"content\":\"${CURRENT}\",\"ttl\":60,\"proxied\":false}"
logger "ddns: ${RECORD_NAME} 已更新为 ${CURRENT}"
fi
配一个 systemd timer 或 crontab,每两分钟跑一次即可:
*/2 * * * * /opt/ddns/update.sh >/dev/null 2>&1
踩过的坑
- 临时隐私地址。系统默认会生成 privacy extension 地址且优先使用,它的生命周期更短。如果你想让解析指向稳定的 EUI-64 地址,取地址时要加过滤,或者在系统层关闭临时地址的"优先外发"。
- proxied 必须为 false。开了 Cloudflare 的橙色云,回家流量会走 CDN,非 HTTP 端口直接不可用。家庭服务一般只用 DNS-only。
- 防火墙别忘了。IPv6 是端到端的,光伏路由器/光猫的 IPv6 防火墙得放行对应端口,否则解析对了也连不上。
- TTL 不是越小越好。60 秒已经够用;再小意义不大,反而增加查询量。
做完之后
现在前缀怎么换都无所谓了:最多两分钟,域名就指向新地址。这类小工具最大的价值不在技术难度,而在于它把一件"偶尔坏一次就要排查半天"的事,变成了完全不用想的事。
生活秩序感,有时候就是这样一点点攒出来的。