# TamaBox 双 CDN 接入指南:境内腾讯 EdgeOne + 境外 Cloudflare > 目标:中国大陆访客走腾讯 EdgeOne 节点加速,境外访客走 Cloudflare 边缘网络加速,共用同一域名与源站。 > 代码前置:`internal/shared/ip.go` 自 **2026-09-03** 起在 `trust_proxy = true` 时信任 EdgeOne 权威客户端头 `EO-Client-IP`(等效 `CF-Connecting-IP`)。单 EdgeOne 或双 CDN 部署均可直接用本文件配套。 --- ## 0. 总体架构 ``` ┌──────────────── DNS(DNSPod 智能解析)─────────────────┐ │ 线路「境内」 → CNAME → EdgeOne 分配的 CNAME │ │ 线路「境外」 → CNAME → Cloudflare for SaaS CNAME │ │ 线路「默认」 → CNAME → Cloudflare(未匹配线路兜底) │ └───────────────┬───────────────────┬────────────────────┘ ▼ ▼ EdgeOne(腾讯,大陆节点) Cloudflare(全球边缘) │ │ └─────► 源站(仅放行两家回源网段)◄──────┘ TamaBox (Flamego) + PG + Redis ``` 核心思路:**CDN 各自接入,DNS 按线路分发**。两家 CDN 都不需要托管你的主域名 NS,主域名 NS 全程留在 DNSPod(腾讯云解析),靠「境内 / 境外 / 默认」三条线路把用户分给不同的 CNAME 目标。 ## 1. 前置条件(逐项确认) | # | 条件 | 说明 | |---|------|------| | 1 | 域名已 **ICP 备案** | EdgeOne 加速区域含「中国大陆 / 全球可用区」时的硬性要求,不可绕开 | | 2 | 腾讯云账号已实名 | 开通 EdgeOne 需要 | | 3 | Cloudflare 账号 | Free 套餐即可(见步骤 3 的 SaaS 说明) | | 4 | **一个闲置域名(或其子域)用于 Cloudflare 承载 zone** | Cloudflare for SaaS 需要一个已托管在 CF(NS 迁过去)的域名作为“承载 zone”,主域名不需要迁 NS。没有就考虑末尾的备选方案 C | | 5 | 源站公网可达 | 回源协议 / 端口按你的部署调整 | ## 2. 步骤 1:DNS 迁到 DNSPod 并建立分线路骨架 1. 在 DNSPod(腾讯云解析)添加你的域名,按提示把注册商处的 NS 改成 DNSPod 提供的 `xxx.dnspod.net` 两条。 2. 等解析生效后,先**建立保底记录**,避免切换期中断: - 线路「默认」:`www` → CNAME → EdgeOne(或先保持源站 A 记录,逐步切); - 建议把各记录 **TTL 临时调小(如 60 秒)**,切换完成后再调回。 3. ⚠️ 若根域(`@`)承载邮箱(存在 MX 记录),**不能**给 `@` 配 CNAME(与 MX 冲突)。此时只对 `www` / 业务子域做 CDN 加速,`@` 保持 A 记录直连或做 301 跳转。 ## 3. 步骤 2:EdgeOne 接入(CNAME 模式) 1. 控制台 → 添加站点 → 输入主域名 → 选购套餐 → **加速区域:中国大陆可用区**(境外交给 Cloudflare)。 2. **接入模式选 CNAME 接入**(主域名 NS 在 DNSPod,不能用 NS 接入)。 3. 归属权验证:按控制台提示在 DNSPod 添加 TXT 或做文件验证。 4. 添加加速域名:在「域名服务 → 域名管理 → 添加域名」里填业务主机名(`www` 或 `@`)+ 源站 IP/域名。 5. 平台分配一个 CNAME 地址(形如 `xxx.edgeone.net`),**先记下来**,步骤 5 统一填到 DNSPod。 6. 证书:开启「站点加速 → HTTPS」自动证书(免费)。 ## 4. 步骤 3:Cloudflare for SaaS(主域名不迁 NS 的关键) Cloudflare Free/Pro/Business 套餐的普通接入要求把整个 zone 迁到 CF 的 NS,与分线路解析冲突。**Cloudflare for SaaS**(Free 套餐含 100 个自定义主机名免费)允许主域名不迁 NS、仅用 CNAME 挂到 CF: 1. 用闲置域名(如 `cf-host.example`)在 Cloudflare 添加 zone,并把该域名的 NS 迁到 Cloudflare(承载 zone,仅此域名迁)。 2. 承载 zone 内:`SSL/TLS → Custom Hostnames(SSL for SaaS)→ Add Custom Hostname`,填入你的**主域名**(如 `www.example.com`)。 3. 按提示完成归属验证:CF 会给出一条验证 TXT(加到 DNSPod)+ 一个 **CNAME 目标值**(形如 `xxx.1234.cdn.cloudflare.net`),记下来用于步骤 5 的境外线路。 4. 等待证书签发至状态 `Active`(通常数分钟到数小时)。之后 CF 自动管理该域名的证书与续期。 > 备选:若不想多养一个域名做承载 zone,可退回**方案 C**——EdgeOne 加速区域直接选「全球可用区」,一家覆盖全球,不做 Cloudflare(代价:境外节点质量与覆盖面不如 CF)。或评估 Cloudflare Business(约 $200/月)的官方 CNAME 接入。 ## 5. 步骤 4:DNSPod 分线路记录(最终形态) 同一主机名、不同线路分别建行(全部 CNAME,避免 A/CNAME 冲突提示): | 主机记录 | 线路 | 类型 | 记录值 | 说明 | |---|---|---|---|---| | `www` | 境内 | CNAME | `xxx.edgeone.net`(EO 分配) | 大陆走 EdgeOne | | `www` | 境外 | CNAME | `xxx.…cdn.cloudflare.net`(SaaS 分配) | 境外走 Cloudflare | | `www` | 默认 | CNAME | `xxx.…cdn.cloudflare.net` | 未匹配线路(如部分境外 DNS)兜底 | > 验证:境内 `dig www.example.com`(或 `nslookup`)应解析出 EdgeOne CNAME;用境外 DNS(如 `dig @8.8.8.8`)应解析出 Cloudflare CNAME。不同递归节点可能有缓存,等待 TTL 过期后复测。 ## 6. 步骤 5:源站锁定(安全前提,不可跳过) `trust_proxy = true` 意味着应用信任 CDN 写来的客户端头,**前提是请求只能来自这两家 CDN**: 1. 从官方渠道取回源网段并配置防火墙 / 安全组,仅放行: - Cloudflare 官方 IP 段:https://www.cloudflare.com/ips/ (`cloudflare.com/ips-v4`、`ips-v6`) - EdgeOne 回源 IP 段:腾讯云 EdgeOne 文档「回源 IP 网段」页(会动态更新,建议做成定期同步或至少配置时核对) 2. 放行你的运维入口(SSH 管理 IP)与回源端口(80/443),其余一律拒绝直连。 3. 回归验证:跑一次仓库自带的 `dev/security-selfcheck.sh`,确认带伪造 `CF-Connecting-IP` 的直连请求已被防火墙拦下(应无法到达应用)。 ## 7. 步骤 6:真实 IP 传递(EdgeOne 侧关键配置!) EdgeOne **默认不携带** `EO-Client-IP`,必须在控制台开启,否则经 EdgeOne 的请求到源站后拿不到客户端真实 IP(自动封禁会误伤所有大陆访客): - 站点级:`站点加速(全局)→ 网络优化 → 客户端 IP 头部配置 → 开启`,**头部名称务必填 `EO-Client-IP`**(代码固定读这个名字,大小写敏感)。 - 或域名级:`规则引擎 → 创建规则(Host 匹配该域名)→ 操作:客户端 IP 头部 → 开启 → 名称填 `EO-Client-IP``。 Cloudflare 侧无需额外配置:`CF-Connecting-IP` 默认就有且恒被 CF 覆盖为真实客户端 IP。 ## 8. 步骤 7:防“借道伪造”剥离规则(双 CDN 特有安全约束) 两家并存时出现新的伪造面:**攻击者借道 CDN-A 时携带 CDN-B 的权威头**。例如客户端经 Cloudflare 访问时自行塞一个 `EO-Client-IP: 任意IP`,若 CF 透传该头到源站,代码会信任它 → 可绕过限流 / 自动封禁 / 单 IP 投票限制,或嫁祸他人。因此必须在两家 CDN 的**回源请求**上剥离对方权威头: | CDN | 剥离动作 | 位置 | |---|---|---| | Cloudflare | 回源前删除请求头 `EO-Client-IP` | `Rules → Transform Rules → Modify Request Header`(Free 套餐可用,规则数有限但只建一条即可) | | EdgeOne | 回源前删除请求头 `CF-Connecting-IP` | `规则引擎 → 操作:修改回源请求头 → 删除 CF-Connecting-IP`(或站点全局回源请求头设置) | > `X-Forwarded-For` / `X-Real-IP` 不需要在此剥离:代码只在 TCP 对端落入 `trusted_proxies` 时才解析它们,两家 CDN 边缘 IP 不在清单内,天然不可信。 > > 验证方法:分别经两家 CDN 发起带伪造头的请求(如 `curl -H 'EO-Client-IP: 203.0.113.66' https://www.example.com/`),在源站访问日志里确认最终到达的请求**不包含**伪造的对方头,且应用记录的客户端 IP 为你的真实出口 IP。 ## 9. 步骤 8:应用配置(conf/app.ini) ```ini [security] trust_proxy = true ; trusted_proxies 仅用于“你自己的”反代/LB(如本机 nginx 127.0.0.1), ; 双 CDN 场景不需要也不应把两家 CDN 的全量网段填进来(代码直接信任 CF-Connecting-IP / EO-Client-IP)。 trusted_proxies = [server] ; 若用防火墙按网段放行回源,可继续监听 :443;若希望更严,可只监听本机 + 本机回源代理。 listen = ``` 行为(2026-09-03 起):`trust_proxy = true` 时 `ResolveClientIP` 依次信任 `CF-Connecting-IP` → `EO-Client-IP` → (仅当 TCP 对端在 `trusted_proxies` 内)解析 XFF/X-Real-IP → 最后回退 RemoteAddr。 ## 10. 步骤 9:缓存与动态内容 TamaBox 是动态应用: - 两侧都**不要缓存 HTML / 页面 / API 响应**;只缓存静态资源。 - EdgeOne:缓存规则命中 `/static/*` 与带版本号资源即可; - Cloudflare:`Caching → Cache Rules` 对 `/static/*` 开缓存,其余 `Bypass`。 - 若将来启用 WebSocket 长连接:Cloudflare Free 支持;EdgeOne 需在站点配置里显式开启 WebSocket。 - 注意两侧的 TLS 版本 / HTTP/3 行为差异属正常现象,与应用无关。 ## 11. 上线切换与回滚 1. 先切「默认」线路 → 观察 15~30 分钟(错误率、日志中的真实 IP 是否正确)。 2. 再切「境内」线路(EdgeOne CNAME 生效)→ 用大陆网络(或大陆 DNS 递归查询)验证。 3. 最后切「境外」线路到 Cloudflare SaaS CNAME → 境外访问验证 + 证书状态 `Active`。 4. 回滚:把对应线路的 CNAME 改回原记录即可,秒级生效(DNS TTL 决定传播速度)。 ## 12. 故障排查速查 | 现象 | 检查 | |---|---| | 大陆访问慢 / 未走 EO | DNSPod「境内」线路是否已配 EO CNAME;`nslookup` 从大陆递归查询结果 | | 境外访问失败 / 证书错误 | SaaS Custom Hostname 状态是否 `Active`;境外线路 CNAME 是否拼错 | | 自动封禁误封 / 限流全走同一个 IP | EO「客户端 IP 头部」是否开启且名称是否为 `EO-Client-IP`;`trust_proxy` 是否 true | | 应用日志里出现伪造 IP | 两家剥离规则是否生效;防火墙是否真的挡住了直连 | | 根域无法配 CNAME | 根域有 MX → 只加速 `www`/业务子域,`@` 保持 A 记录 | ## 13. 相关代码与文档 - 代码改动:`internal/shared/ip.go`(`EO-Client-IP` 信任分支)、`internal/shared/ip_test.go`(新增 4 条用例)、`internal/conf/static.go` 与 `conf/app.sample.ini`(注释同步)。 - 单 CDN Cloudflare 加固参考:`outputs/cloudflare-origin-lockdown-guide.md`。 - EdgeOne 客户端 IP 头部(腾讯官方文档,会更新):「携带客户端 IP 头部回源」。 - Cloudflare for SaaS 官方文档:developers.cloudflare.com → SSL/TLS → SSL for SaaS。