# Vercel Deploy Bind Domain

> 在无浏览器/agent 环境下用 Vercel CLI 部署本地前端项目，并把自有域名或子域绑上去。适用于「帮我部署到 Vercel」「把域名绑到 Vercel」「Vercel 登录要我授权」「vercel domains 要加什么 DNS 记录」等场景，含 device-flow 登录、npm 缓存权限绕过、以及读取所需 CNAME 的正解。

- Skill: `paloma333/vercel-deploy-bind-domain` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paloma333/vercel-deploy-bind-domain`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paloma333/vercel-deploy-bind-domain/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: Paloma333 (https://skillmd.com/u/paloma333)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/paloma333/vercel-deploy-bind-domain

---


# 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`
```bash
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 自动签发证书。别以为加完就完了。

## 标准流程

```bash
# 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 记录后，按这个顺序验，能一眼分辨是**路由问题**还是**证书等待**：

```bash
# ① 权威 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://`。

## 坑

1. **`~/.npm` 权限异常**（报 EACCES / ownership，提示 `sudo chown -R ...`）
   → **不要 sudo**。用 `npm_config_cache=/tmp/npmcache npx -y <pkg>` 绕过，export 一次后续复用。
2. **curl 自检前先看有没有代理**：沙箱/本机常有 `HTTP_PROXY`，必须 `--noproxy '*'`，
   否则测到的是代理不是站点。`env | grep -i proxy` 确认。
3. **目录返回 404 不代表部署坏了**：没有 `index.html` 的目录本来就 404（例如 `/gephi/`）。
   要测具体文件（`/gephi/data.json`），别慌着重部署。
4. **别用「本地测很快」下结论**：先确认出口位置
   `curl -s --noproxy '*' https://www.cloudflare.com/cdn-cgi/trace | grep -E '^(loc|colo)='`。
   在中国香港/海外测出来的速度与大陆用户无关。
5. **有未 push 的提交时，控制台 Git 导入会部署旧版本**：要么先 `git push`，要么用 CLI 从本地部署。
6. **Vercel 免费版（Hobby）不含中国大陆节点**，大陆流量会被路由到境外（实测曾落到美国 SJC）。
   要给大陆用户稳定的速度，只有境内节点（需 ICP 备案）。别把「绑了自定义域名」当成国内加速。
7. **`vercel.json` 的 `headers[].source` 不接受含 `/` 或 `|` 的自定义正则**：
   `"/assets/:file([^/]*\\.(js|css))"` 这类写法部署前校验就失败，报
   `Error: Header at index N has invalid source pattern`。只能用 `(.*)` 这类前缀通配。
   要区分「带 hash 的构建产物」与「无 hash 的 public 资源」时，要么接受统一策略，
   要么把构建输出改到独立前缀（如 Vite 的 `build.assetsDir`），靠前缀而非正则区分。
8. **默认不带长缓存，重资源站点尤其吃亏**（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` 逐条复验。

