邮箱验证:六位验证码 vs 确认链接(2026)
对比六位邮箱验证码与确认/魔法链接:选型表、钓鱼与投递差异,以及同一条 POST /emails 发送路径。
大多数产品会做两次的选择
几乎每个 SaaS / AI 应用迟早都要证明同一件事:「这个邮箱属于正在用产品的人」。主流有两种形态——六位验证码与确认 / 魔法链接——团队常先抄教程里的第一种,等移动端体验、钓鱼投诉或企业邮箱过滤器出现后才后悔。
无论哪种,Rovela 的职责相同:用 POST /emails 投递事务邮件。应用负责生成密钥、存哈希、做过期与一次性核销,以及校验用户输入或点击。Rovela 没有独立的「OTP 产品」或「魔法链接产品」——只有发送 API,加上文档里的安全模式。
本文是对比文:何时用码、何时用链接,以及投递与客户端怪癖如何 tipping。完整实现见下方兄弟文与文档——这里用来选型,再深链进去。
「验证码」和「链接」分别指什么
六位验证码(OTP / 邮箱验证码)
- 应用用密码学安全随机源生成短数字(或字母数字)一次性值。
- 存 HMAC 等带密钥哈希、过期时间与尝试次数——不存明文(邮箱验证码示例)。
- 用户在你的界面输入(或从通知 / 主题行读取)。
- 实现深挖:用邮件 API 发 OTP、邮箱 OTP 安全。
确认链接或魔法链接
- 应用生成不可猜测的长 token(例如 32 字节随机,
base64url),存哈希、短过期、核销即作废(密码重置链接——同一 token 形态)。 - 邮件里放指向你自己源站的绝对 URL;用户点击。
- 变体:邮箱归属确认、无密码魔法登录、重置、邀请。深挖:邮箱验证链接、魔法登录邮件链接、安全的密码重置邮件、Next.js 魔法登录。
从 ESP 视角,两者都是「发验证邮件 API」问题:一次 HTTP 发送、html + text、已验证域名、幂等重试。
决策表
| 维度 | 六位验证码 | 确认 / 魔法链接 |
|---|---|---|
| UX 摩擦 | 输入 6 位(或粘贴) | 一次点按 / 点击 |
| 移动端 | 数字键盘;邮箱与 App 分离时仍可用 | 邮件客户端能打开 HTTPS 时很爽;需手动切回 App 时别扭 |
| 桌面端 | 从另一标签复制或重输 | 收件箱已开着时通常最快 |
| 钓鱼 / 社工 | 用户习惯在你的界面输码;仍怕「把码填到这里」的诱导 | 用户习惯点链接;更易伪造「验证」按钮;需教育「我们不会在流程外索要原始 token」 |
| 邮件客户端改写 | 很少影响正文/主题里的数字 | 安全链接扫描、代理,以及点击追踪可能包装或预取 URL |
| 无障碍 | 跨设备输入对部分用户难;数字对读屏友好(标记得当) | text 里放完整 URL 有帮助;HTML 链接文案要清晰 |
| 短信兜底 | 易与同码通道对齐 | 短信也能带链接,但长 URL 易折行;短信更偏短码 |
| 限流 / 爆破 | 空间极小 → HMAC + 尝试上限 + 重发节流是刚需 | Token 空间巨大;仍要限制签发与核销频率 |
| 过期 | 常见 5–15 分钟 | 归属确认可达数小时;登录/重置常见 15–60 分钟 |
| 一次性 | 成功后删除(或轮换) | 单条 SQL 核销即删 |
| 投递 / 扫描器 | 正文短;主题放码利于预览;少「引诱点击」风险 | URL 更长;预取机器人可能撞链接——核销应对 GET 安全,或要求显式确认 |
| 共用 / 一体机 | 更好:若只证明「在场」,不会因一点击就意外登录 | 魔法登录在共用设备上更危险,除非会话 UX 谨慎 |
什么时候验证码更合适
优先六位(或短字母数字)码,当:
- 高风险二次验证——敏感操作(导出、改账单、新设备)前要证明「此刻能读到这封邮件」。
- 共用或受监督设备——一点击登录可能悄悄开会话;在 App 里输码意图更清晰。
- 不信任链接的用户——金融、医疗、安全敏感人群常拒绝点击。
- 跨通道一致——已有或将有短信/语音同码;心智模型统一。
- 以 App 为中心的移动流程——人留在 App;邮件只是传数字的旁路(主题行可选)。
什么时候链接更合适
优先确认或魔法链接,当:
- 个人设备上的无密码登录——从收件箱一键进会话(魔法登录邮件链接)。
- 邮箱归属 / 双重确认——要的是持久「这地址是我的」,不是 10 分钟「在场」(邮箱验证链接)。
- 桌面低摩擦注册——另一标签已开着收件箱。
- 更换邮箱——链接发到新地址;核销后再切换。
- 密码重置——URL 里放长 token 是文档模式(安全密码重置)。
混合模式(谨慎使用)
生产里常见的诚实混合:
- 同一封邮件里码 + 链接——「输入
482913或在浏览器确认」。设备不可预知时有用。代价:要签发、限流、过期两套秘密;意图要说清(同一会话目的)。 - 归属用链接,登录用码——干净常见:一次链接验证地址,之后登录/升权用 OTP。
- 链接落到仍要输码的页面——通常更差;除非合规强制,否则别这么做。
混合不是 Rovela 功能——仍是应用设计,发送还是一次 POST /emails。
会改变选型的投递与客户端细节
安全类邮件与 newsletter 的失败方式不同:
- 验证码更短。 HTML 更少,text/html 更易对齐;主题放码利于锁屏预览(验证码文档)。高度共享屏幕上可省略主题码。
- 链接会招扫描。 企业「安全链接」、杀毒抓取、部分客户端会预取
GET。若裸 GET 就登录或核销,预取可能烧掉 token 或建会话。更宜 POST 确认,或让 GET 只展示「继续」而无副作用。 - 点击追踪会改写 URL。 密码重置文档提醒:追踪会把链接包到追踪主机;虽仍可用,但安全 token 应直达你的应用——认证邮件用关闭点击追踪的发送域/子域(密码重置示例)。
- 务必发
text,带完整 URL 或验证码。 会剥 HTML 链接的过滤器仍能留下纯文本。 - 声誉隔离。 认证信从子域发(
security@mail.example.com),别和营销共命运(为什么验证码/魔法登录进垃圾箱)。
同一条发送路径:POST /emails
正文是数字还是 <a href>,线路格式相同。在已验证域名上,用 sending_access 密钥(鉴权、快速开始):
curl -X POST https://api.rovela.dev/emails \
-H "Authorization: Bearer re_your_sending_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: verify-user_123-2026-11-04" \
-d '{
"from": "Acme <security@mail.acme.com>",
"to": "alice@example.com",
"subject": "Confirm your Acme email",
"html": "<p>Your code is <strong>482913</strong> (10 minutes).</p><p>Or <a href=\"https://app.acme.com/verify?token=…\">confirm in the browser</a>.</p>",
"text": "Your code is 482913 (10 minutes).\n\nOr confirm: https://app.acme.com/verify?token=…"
}'
两种形态通用注意:
200表示已入队,不等于进收件箱——看消息事件或 Webhook(API: emails)。Idempotency-Key(≤256 字符,24 小时)避免 worker 重试连发两封。- 抑制:
400「recipient is suppressed」时不要假装已发码/链接——换地址。 - 量级与套餐:见定价;本文不编造额度。
AI 产品语境(码、邀请、登录链接作为一组)见面向 AI 应用的事务邮件。
2026 产品的建议默认
| 流程 | 默认 | 原因 |
|---|---|---|
| 注册邮箱确认 | 链接(约 24 小时级过期) | 归属证明,桌面摩擦低 |
| 无密码登录 | 魔法链接 或 验证码 | 消费级偏链接;共用设备 / 不信链接则偏码 |
| 升权 / 敏感操作 | 验证码 | 证明在场,避免邮件一点击就建持久会话 |
| 密码重置 | 链接 | 与文档长 token 模式一致 |
| 接受邀请 | 链接 | 动作是「加入组织」,不是「输六位数」 |
若工单堆在「码没收到」或「我不敢点」——常常是投递问题(垃圾箱指南),不一定是 UX 原语选错。
本文不是什么
- 不是完整 OTP 实现(见 send-otp + 文档)。
- 不是完整魔法登录 / 验证链接教程(见对应博客)。
- 不宣称 Rovela 替你存或校验码/token。
- 不是国内厂商对比或编造价格。
下一步
- 用上面的表选型。
- 应用侧秘密:验证码 或 密码重置 / 链接模式。
- 投递:POST /emails · 快速开始。
- 投递加固:验证码/魔法登录进垃圾箱。
- 额度:定价。
一句话: 六位验证码优化在场证明与对链接的不信任;确认链接优化摩擦与持久归属。发验证邮件 API 相同——Rovela 入队并做域名认证投递;密码学在你的应用里。