Vercel 部署 + 绑定自有域名(无头环境)
核心认知(先看这几条,能省掉大半试错)
1. 无头登录走 device flow,不需要用户交出密码或 token
vercel login 在检测到 agent 时会自动进入 --non-interactive,打印一个 device URL 并轮询等待:
Visit https://vercel.com/oauth/device?user_code=XXXX-XXXX
⠋ Waiting for authentication...
做法:后台运行该命令 → 读出 URL 交给用户 → 用户在自己浏览器点一次「Authorize」→ 进程自己结束并打印
Congratulations! You are now signed in.
登录态落在 ~/.vercel/auth.json(可用 -Q/--global-config 改位置)。之后所有命令都能直接用。
注意:CLI 59 的
login已没有--github/--google这类 provider 参数,别去传。
2. 新项目给的是「项目专属」CNAME 目标,不是 cname.vercel-dns.com
形如 <hash>.vercel-dns-017.com。
76.76.21.21 与 cname.vercel-dns.com 是 Vercel 的旧记录(官方提示已扩展 IP 段,旧值仍可用但不建议)。
网上教程大多照抄旧值——照抄会多一层解析坑。
3. 读所需 DNS 记录的正解是 vercel domains verify
vercel domains verify <sub.domain.tld>
它会列出「项目归属 ✔/✘」和「DNS 配置 ✔/✘」,并给出要加的具体记录,比猜或翻文档可靠。
4. 根域(apex)不要用 CNAME
DNS 规范:CNAME 不能与其他记录共存,挂在 apex 会和 MX/TXT/NS 打架。apex 用 A 记录,子域才用 CNAME。
5. 用子域试,主域留白
绑 preview.example.com 而不是 example.com:
- 不影响后续做 ICP 备案(备案审核期最好让主域零解析)
- 想撤就删一条记录,不留后患
6. domains add 只做归属登记,不等于 DNS 生效
添加成功的下一步必然是「用户在 DNS 服务商加记录」,之后 Vercel 自动签发证书。别以为加完就完了。
标准流程
# 0. 绕过 ~/.npm 权限问题(见坑 1)
export npm_config_cache=/tmp/npmcache
# 1. 预热 + 确认可用
npx -y vercel@latest --version
# 2. 登录(后台跑,把 device URL 给用户)
npx -y vercel@latest login
# 3. 确认身份与已有项目
npx -y vercel@latest whoami
npx -y vercel@latest teams ls
npx -y vercel@latest projects ls
# 4. 部署到生产(首次自动建项目,项目名默认取目录名)
npx -y vercel@latest deploy --prod --yes
# 5. 自检生产 URL 的关键路径
curl -s --noproxy '*' -o /dev/null -w "%{http_code}\n" https://<prod>.vercel.app/
# 6. 把域名加到项目
npx -y vercel@latest domains add <sub.domain.tld> <project>
# 7. 读出要加的 DNS 记录
npx -y vercel@latest domains verify <sub.domain.tld>
# 8. 用户加完记录后复验
npx -y vercel@latest domains verify <sub.domain.tld>
dig +short CNAME <sub.domain.tld>
curl -s --noproxy '*' -o /dev/null -w "%{http_code}\n" https://<sub.domain.tld>
验证与排障顺序(别把「证书还没签发」当成配置错)
加完 DNS 记录后,按这个顺序验,能一眼分辨是路由问题还是证书等待:
# ① 权威 NS 是否已生效(绕开递归缓存)
dig @<你的NS域名> <sub.domain.tld> CNAME +short
# ② 递归解析 + 完整链
dig @8.8.8.8 <sub.domain.tld> +short
# ③ Vercel 侧判定
vercel domains verify <sub.domain.tld>
# ④ 先用 HTTP 验路由(不经 TLS)
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://<sub.domain.tld>/
# ⑤ 最后验 HTTPS
curl -s -o /dev/null -w "%{http_code} ssl=%{ssl_verify_result}\n" https://<sub.domain.tld>/
典型现象:HTTP 200/308 正常,但 HTTPS 失败(http=000、ssl_verify_result=1、
curl exit 35、SSL_ERROR_SYSCALL,openssl 也拿不到证书)
→ 这不是配置错,是 Vercel 还没为这个自定义域名签发证书。
本案例中 DNS 加好 → verify 显示配置有效 → 约 2–3 分钟后证书自动签发(Let's Encrypt),
ssl=0 即生效。别急着重加记录或改配置,先轮询等待。
最终验收要一起看:证书 subject/issuer(openssl s_client ... | openssl x509 -noout -subject -issuer -enddate)、
关键路径 HTTP 200、http:// 能 308 跳 https://。
坑
~/.npm权限异常(报 EACCES / ownership,提示sudo chown -R ...) → 不要 sudo。用npm_config_cache=/tmp/npmcache npx -y <pkg>绕过,export 一次后续复用。- curl 自检前先看有没有代理:沙箱/本机常有
HTTP_PROXY,必须--noproxy '*', 否则测到的是代理不是站点。env | grep -i proxy确认。 - 目录返回 404 不代表部署坏了:没有
index.html的目录本来就 404(例如/gephi/)。 要测具体文件(/gephi/data.json),别慌着重部署。 - 别用「本地测很快」下结论:先确认出口位置
curl -s --noproxy '*' https://www.cloudflare.com/cdn-cgi/trace | grep -E '^(loc|colo)='。 在中国香港/海外测出来的速度与大陆用户无关。 - 有未 push 的提交时,控制台 Git 导入会部署旧版本:要么先
git push,要么用 CLI 从本地部署。 - Vercel 免费版(Hobby)不含中国大陆节点,大陆流量会被路由到境外(实测曾落到美国 SJC)。 要给大陆用户稳定的速度,只有境内节点(需 ICP 备案)。别把「绑了自定义域名」当成国内加速。
vercel.json的headers[].source不接受含/或|的自定义正则:"/assets/:file([^/]*\\.(js|css))"这类写法部署前校验就失败,报Error: Header at index N has invalid source pattern。只能用(.*)这类前缀通配。 要区分「带 hash 的构建产物」与「无 hash 的 public 资源」时,要么接受统一策略, 要么把构建输出改到独立前缀(如 Vite 的build.assetsDir),靠前缀而非正则区分。- 默认不带长缓存,重资源站点尤其吃亏(2026-09 实测):Vercel 对静态文件默认发
cache-control: public, max-age=0, must-revalidate,连带 hash 的构建产物也一样, 回访时会逐文件 revalidate(304 也要一个来回)。用vercel.json的headers补: hashed 产物max-age=31536000, immutable、无 hash 的素材给中间值 +stale-while-revalidate; HTML 保持must-revalidate不要动。部署后必须curl -sI <url> | grep -i cache-control逐条复验。