一、为什么合规是 P0:法务红线只有一条,但踩了就出局

冷邮件合规三件套架构总览

欧美市场对未请求商业邮件(UCE)的执法逻辑非常朴素:接收方必须有低成本、可执行的退出通道。GDPR、CAN-SPAM、CASL 三大法域的具体条款各异,但技术落点高度收敛:

  • 邮件头里要有 List-Unsubscribe(RFC 8058 还要求支持 one-click GET/POST 直接生效,不能只跳一个需要登录的网页);
  • 退订请求要真实执行并持久化,不能”退订了但下个月还在发”;
  • 发件主体(公司名+物理地址)要出现在邮件正文;
  • 供应商(我们用的 Brevo)的投诉/退订事件要能回灌到自己的抑制名单,否则用户通过 Gmail 一键举报,你毫不知情,域名信誉持续失血。

我们的实现选择了自建而不是依赖 ESP 列表托管,核心考虑是联邦式架构:未来客户可能自带发信通道,抑制名单必须长在自己数据库里。

二、三件套的落地结构

2.1 全局退订表 + HMAC 令牌

一张 unsubscribes(email, reason, source, created_at) 表做全局黑名单。每封邮件附带个性化页脚:

[email protected]/u/{token}

token = HMAC(email + 过期时间),复用系统内已有的签名体系,无需登录态。不暴露邮箱明文在 URL 里——这既是防枚举,也是防转发场景下替别人退订的边界问题。

公开路由 GET /u/:token 渲染确认页,POST /u/:token 一键静默退订(one-click 语义,页面上再给一个”确认取消订阅”按钮兜底给禁 JS 的客户端)。部署踩坑:反向代理必须为 /u/ 单独加 location,我们的路由前缀 /v/ 和 /vf/ 已经占了坑位,新公开路由上线后 nginx 404 被 CDN 吞成 52x,排查要从源站端口直接 curl 才看得见真相。

2.2 头部与页脚注入

发送函数统一注入:

List-Unsubscribe: <https://example.com/u/abc123>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

HTML 与纯文本双版本页脚都带公司名+物理地址+退订链接。有个容易被忽略的细节:发送函数如果同时返回”邮件原文”和”消息 ID”,发送日志必须存实际发出的内容而不是模板——我们第一轮 QA 时日志存的是变量占位原文,vision 目检收件箱截图时一度误判页脚没生效,查日志才发现是日志的锅不是发信的锅。

2.3 抑制拦截 + 事件回灌

发送入口先查黑名单,命中直接 409 拒绝并在前端给出友好提示。另一端,定时拉取 ESP 的 unsubscribe/complaint 事件写回同一张表,形成闭环:用户在 Gmail 点”举报垃圾邮件”,24 小时内该地址进入全局抑制,全租户停发。

验证方式是发一封真邮件到自己控制的测试地址(不碰任何真实客户),确认 ESP 接受了带 List-Unsubscribe 的投递——不合规的头部值会让 Brevo 直接 400,能发出去本身就是第一道验证。

三、发信域名预热:新域名的”缓刑期”

新域名直接放量发送,等于拿信誉去赌博。行业通行做法是按天爬坡日发送上限。我们把这条规则做成服务端强制:

WARMUP_RAMP = [3, 5, 8, 12, 18, 25, 35] → 第 8 天起 50/日

两个实现细节:

  1. “第几天”必须按实际发生过发送的 distinct 日期数计算,不能用”注册后自然日”——域名注册了但两个月没发信,中间两个月的”日历爬坡”毫无意义;服务重启也不能影响计数,所以直接查发送日志表。
  2. 超限返回 429 时,文案要教育用户:”预热第 4 天 · 今日上限 12 封”,并在触达工作台顶部放一个实时状态 pill。静默限额只会招来”为什么发不出去”的工单。

四、邮箱真实性分层验证:把”看起来对”和”真的能收到”分开

邮箱真实性分层验证漏斗

此前系统里的邮箱来自两条路径:官网真实抓取(grounded)和按姓名规则拼出来的 pattern 猜测(firstname.lastname@corporate.com)。后者在 UI 上和前者长得一模一样——这就是雷。

新验证腿做成零成本分层(全部走本地解析,不花钱):

层 检查 失败处置
1 RFC 语法 直接 invalid
2 一次性邮箱域名库 disposable
3 MX 记录解析 no_mx → invalid
4 角色地址识别 info@/sales@/support@ 标记 role(能收但不该冷发)
5(可选,默认关) SMTP RCPT 深探:真实建连问服务器”这个收件人存在吗” valid/invalid

关键设计决策:

  • 第 5 层默认关闭。很多接收服务器会对 RCPT 探测直接断连甚至打标,把它当”可选付费能力”而不是默认行为。我们在自己的服务器出口实测了四个象限(语法错误/一次性域名/角色箱/真实个人地址),确认探测可用后仍然保持默认关。
  • 验证结果反哺可触达性评分:已验证邮箱 +15 分,一次性/无效直接压到地板。
  • pattern 猜测邮箱不删除、要展示——它是线索(告诉你这家公司的邮箱格式),但 UI 必须戴帽:⚠ 未证实,并且服务端对未证实邮箱发信直接 422 拒绝,显式 force_pattern 参数才能绕过,绕过后前端还有一道风险确认勾选框。宁可用两道摩擦劝退误发,不做”看起来能发”的顺从。

五、弹药审计:冷水泼出来的真相

做完以上所有,我们跑了一次”真实战役就绪度盘点”,结果是公开处刑:

  • 看板 9 个 A 级商机,全部是早期内部测试攒下的 SaaS 公司,实锤邮箱 0 个,29 个可用地址全是 pattern 猜测或角色箱;
  • 给真实付费客户的名单做深度背调升级(9 家、消耗 45 积分、11/11 成功零失败)后,全库仅 2 个地址通过”MX+SMTP 双实锤”——其中 1 个是某欧洲家居巨头的德国 B2B 公开合作邮箱,性质特殊(明示的 B2B 入口而非 info@ 黑洞),恰好也是海关数据实锤的”直接进口”公司。

这个发现直接改写了行动方针:原本计划”首批发 3-5 封”,实际变成”要么发唯一的 1 发实锤弹药打通全链路埋点,要么先补弹药库再开战役“,决策权交给人类老板。弹药盘点必须在承诺发信计划之前做完,别让计划书写得比弹药库满。

六、经验清单

  1. 合规不是法务部门的事,List-Unsubscribe 两行头 + 一张抑制表 + 事件回灌,一个下午就能上完,晚上一天的代价是域名信誉。
  2. ESP 事件回灌要做成双向闭环:你推给它的每一封,它回给你的每一个投诉,都落在同一张黑名单上。
  3. pattern 猜测邮箱的价值是线索不是弹药,UI 诚实标注 + 服务端硬拒绝,比”发出去试试”便宜一万倍。
  4. 新域名日限额爬坡要按”真实发送日”计算,重启安全,文案要会自我解释。
  5. 上线任何”触达”功能之前,先盘点真实弹药库——可安全冷发的邮箱数量,是这门生意唯一诚实的资产负债表。