把项目从 xxx.pages.dev 换到 www.自己的域名.com,看起来只是"加一条 CNAME"。我们真做了这件事,把坑踩完之后,结论有点反直觉:能自动化的部分不多,容易误判的地方很多。下面按「前置检查 → 执行 → 硬坑 → 验收」拆开,所有踩点都是实测。
接手站点时我们先数了 Cloudflare 账号下的 zone 数:0。
这不代表 CF 没配好,而是说明域名的 DNS 托管在别处(国内常见是腾讯云 DNSPod,或阿里云/万网)。CF 手里只有一个 Pages 权限的 token。
连锁后果,都别再试:
httpRequests* 类指标查不到;pagesFunctionsInvocationsAdaptiveGroups 直接返回 authorization denied;Authentication error 10000;zone.create(在 CF 里新增托管域名),报 Requires permission "com.cloudflare.api.account.zone.create"。curl -s "https://api.cloudflare.com/client/v4/accounts/$ACC/pages/projects" \ -H "Authorization: Bearer $TOKEN" | head -c 400
能读 Pages 项目、能 POST /pages/projects/{name}/domains 绑定自定义域,这一步就够用(实测可用)。其余权限不要假设。
nslookup -type=NS 你的域名 8.8.8.8
dns*.hichina.com → 阿里云(万网);dnspod.net → 腾讯云 DNSPod。判明托管商,再决定走网页控制台还是 API AccessKey。没有登录态或密钥就是没有,别硬试。
curl -sL "https://rdap.verisign.com/com/v1/domain/EXAMPLE.COM"
关注 status(active = 正常)、registration、expiration。RDAP 是 Verisign 权威接口,无需登录就能拿到状态与关键日期:未过期 → 标准续费即可,无赎回费;过期 0~30 天 → 宽限期,仍可原价续;过期 30~60+ 天 → 赎回期,费用明显更高。
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACC/pages/projects/$PROJ/domains" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"www.example.com"}'
返回 status: initializing + validation_data.method: http,说明走 CNAME 即可,不需要 TXT 验证。两个都要绑:www.example.com 和裸域 example.com——用户习惯直接输不带 www 的地址。
目标:www → CNAME <project>.pages.dev。动手前必须确认两件事:
DeleteDomainRecord(RecordId=...) # 原 A 记录 AddDomainRecord(RR='www', Type='CNAME', Value='<proj>.pages.dev', TTL=600)
阿里云实测允许 @ 加 CNAME,所以裸域也能直接指 CF;若添加失败,立即回滚恢复原 A 记录——脚本里写成 try/rollback,别指望人记得。
for i in range(20):
r = cf('/accounts/%s/pages/projects/%s/domains' % (ACC, PROJ))
st = {d['name']: d['status'] for d in r['result']}
code = curl('https://www.example.com/')
if st.get('www.example.com') == 'active' and code == '200':
break
time.sleep(20)
initializing / pending 阶段 HTTP 返回 000(证书未就绪)或 403(CF 回源到旧站)都是正常的中间态,别误判成失败。active 之后立刻 200。
CF Pages 的 _redirects 不支持 host 匹配,所以用 Functions 中间件:
export async function onRequest(context) {
const url = new URL(context.request.url);
if (url.hostname === 'example.com') { // 裸域
url.hostname = 'www.example.com';
return Response.redirect(url.toString(), 301);
}
return context.next();
}
重新部署后验证 curl -sI https://example.com/contact → 301 + Location: https://www.example.com/contact。带路径也要正确转发,只测首页等于没测。
sitemap.xml 里所有 <loc> 域名;robots.txt 的 Sitemap: 行;<link rel="canonical">;<meta property="og:url">;functions/api/lead.js 里的 SITE_DOMAIN);functions/ 不参与编译。判据:输出出现 Uploading Functions bundle;若看到 Compiled Worker successfully,说明中间件也编译了。wrangler pages project create 报 code 8000000 → 改用 CF API POST /pages/projects 创建即成功。/products):CF 对 /xxx.html 会 308 跳转,白白多一跳。pending 期间不要反复删了重绑,耐心轮询;反复重绑反而拖慢签发。ipconfig /displaydns | Select-String -Pattern "域名" -Context 0,6 # 典型症状:同一名字既有 CNAME→xxx.pages.dev,又有 A→旧服务器 IP ipconfig /flushdns判断是不是本地问题:用公共 DNS 对比(
nslookup 域名 8.8.8.8 / 223.5.5.5)。公共 DNS 正常而本机异常 → 就是本地缓存,不用改线上配置。ipconfig /flushdns 会静默失败:MSYS 会把 /flushdns 当路径转换掉,命令实际没执行(屏幕上输出的是 ipconfig 帮助信息,很容易被当成"已经刷了")。正确写法:用 PowerShell 直接执行,或 cmd //c "ipconfig /flushdns"。| 检查项 | 期望值 |
|---|---|
https://www.域名/ 及全部子页 | 200 |
https://域名/ | 301 → www(带路径同样转发) |
*.pages.dev | 200(保留可用,可留作回退) |
/api/xxx 的 GET | 405(证明边缘函数存活) |
| canonical / sitemap / robots | 全部指向新域名 |
| 证书 | 浏览器无告警,curl -sI 正常 200 |
验收里最容易被跳过的是 /api/xxx 那一条。GET 返回 405 恰恰是好消息——说明 Functions 已经跟着新域名一起生效了;如果返回 404,多半是 functions/ 没被编译进去。
把这些写进文档不是为了少干活,而是为了让「卡住」这件事本身可定位。工程里最贵的往往不是难题,而是分不清"在等外部"和"我没做对"。
不必。CF Pages 绑定自定义域后返回 validation_data.method: http,说明只要在原 DNS 服务商处加一条 CNAME → <project>.pages.dev 即可,NS 可以留在 DNSPod 或阿里云。
不是。initializing/pending 阶段 HTTP 返回 000 或 403 都属正常中间态。建议以 20 秒间隔轮询,通常 1~3 分钟转 active,之后立刻 200。反复删绑反而会拖慢签发。
两个都要绑。裸域同样在 DNS 加 CNAME(阿里云实测允许 @ 加 CNAME);若原记录承载邮件(有 MX),就不要动裸域。再用 Functions 中间件把裸域 301 到 www,注意带路径也要正确转发。
先查本机 DNS 缓存:旧 A 记录与新 CNAME 会并存于缓存中,在旧记录 TTL 到期前被随机命中。用 ipconfig /displaydns 看是否同时存在两类记录,再用公共 DNS(8.8.8.8 / 223.5.5.5)对比——公共 DNS 正常而本机异常,就是本地缓存问题,不必改线上配置。
ipconfig /flushdns 没反应?MSYS 会把 /flushdns 当作路径转换掉,命令实际没有执行,屏幕上只剩 ipconfig 的帮助信息,很容易被忽略。改用 PowerShell 执行,或 cmd //c "ipconfig /flushdns"。
上线一个自有域名,技术动作不多,但每一步都有一个"看着像故障、其实不是"的中间态。把中间态写清楚,比多写十行配置更有价值。
看看我们的静态站交付体检服务 →