跳到正文

Cloud Mail 补记,R2 附件验证与 Resend 密钥轮换

用真实收信确认 Cloud Mail 附件写入私有 R2,核对预览、下载与匿名访问,再记录 Resend 密钥轮换及新旧凭据的发信验证。

归入领域
产品基础设施
阅读时间
11 分钟阅读
发布于
2026.10.13

一句话结论

两份测试附件在远程 R2 中通过了 SHA-256 校验,登录后可预览下载,匿名读取返回 401;Resend 新密钥已同步到 D1 和 KV,并通过应用发信与实际收件验证。

上一篇 Cloud Mail 部署记录 写到了私有 R2 切换。当天盘点过旧数据,D1 没有附件记录,KV 也没有附件或上传背景,所以没有历史文件需要复制。桶创建完成后,里面空着符合当时的情况。

但这还留下了一个问题。生产邮箱真正收到带附件的邮件以后,文件会不会写进 R2,网页登录后能不能打开,别人拿到附件地址能不能读?

2026 年 10 月 13 日,我补做了这轮验证。过程中还发现旧 Resend 发送密钥曾出现在本地工具的错误输出里,于是把密钥轮换也一起处理了。这篇记录接着上一篇往下写,真实邮箱、业务域名、资源标识和对象路径都不公开,也不贴凭据或原始日志。

先发一封真的带附件的邮件

上一轮验证用过远程 R2 绑定,也测过本地附件页面。这次让一封邮件实际经过 Resend 发出,再通过生产环境的收件流程进入 Cloud Mail。

邮件里放了两份专门生成的测试文件。一张 PNG,大小为 31,695 字节,尺寸为 800 × 480;一份 TXT,大小为 136 字节。内容不包含私人信息。

邮件收到后,应用里出现了对应的收件记录和两条附件记录。接下来我直接从远程 R2 下载这两个对象,分别和发送前的原文件计算完整的 SHA-256。两份都一致。

我又检查了 KV,同样的两个附件 key 都不存在。结合远程 R2 中读出的文件,才能确认这轮新附件已经写入 R2,读取成功也没有依赖 KV 回退。

这次仍然没有旧附件迁移。新测试邮件和两份附件保留在系统里,方便后续复查。

桶概览还显示零,文件已经能读到

验证过程中,桶的汇总数字仍然显示 0 个对象、0 B。如果只看这一行,很容易继续怀疑没有写进去。

当时使用的 Wrangler 桶概览读取的是存储分析统计,没有逐个列出当前对象。我另外做的远程对象读取,已经拿到了两份完整文件,而且哈希与原件相同。

这两类结果需要分开记录。文件存在,有直接读取和字节校验的证据;概览数字当时没有刷新。我没有测出它多久会更新,也没有等到统计变成非零才结束验证。

以后再遇到类似情况,我会先查预期对象是否能读出、内容是否匹配,再看概览统计。若对象读取失败,就继续查写入流程和资源绑定,不能拿统计可能延迟当作已经存好的依据。

私有桶之外,还要检查应用的读取入口

桶的 r2.dev 公共访问保持关闭,也没有绑定公开的桶自定义域名。应用通过 Worker 绑定读取文件,浏览器使用邮箱自己的附件入口。

这次同时测了新增的附件 API 和兼容旧邮件的附件路径。普通 HTTP 请求覆盖了两份文件,结果如下。

检查项目新附件 API旧附件路径
有效登录会话读取 PNG 和 TXTHTTP 200,内容与原文件一致HTTP 200,内容与原文件一致
匿名读取 PNG 和 TXTHTTP 401HTTP 401
私有附件缓存策略private, no-storeprivate, no-store

随后在生产网页里打开测试邮件。PNG 预览成功解码,显示尺寸与原图一致;两份附件都通过页面上的下载按钮下载,文件名符合预期,下载文件的 SHA-256 也与原件一致。浏览器没有出现未捕获异常。

预览和下载先发带会话认证头的请求,再用返回内容生成 Blob URL。登录 token 没有放进附件请求的 URL。另一个没有登录状态的干净浏览器访问 PNG 的两个入口,都得到 401。

这轮复用了已有的有效管理员会话。它验证的是有登录身份时的附件操作,没有重新输入密码走一遍登录,也没有用生产环境中的另一个普通账号测试跨账号访问。上一篇记录的归属权限回归测试,和这里的生产验证,覆盖范围各有边界。

旧发送密钥出现在错误输出里,就更换

初次本地发信测试时,工具把一份凭据说明文件的全部内容当成了请求头值。请求还没有发出,参数处理已经报错,旧发送密钥随错误信息出现在工具输出里。

修正读取逻辑,只能避免后续继续输出。已经出现过的旧凭据仍然需要撤销。

这次由我在 Resend 后台删除旧 key,重新创建发送 key,并替换原来的私有凭据文件。接着才把新值同步到应用。因此实际顺序是旧 key 先被删掉,Cloud Mail 的配置随后更新。

对持续承担发信任务的实例,我会把顺序安排成先创建新 key、验证并切换配置,再撤销旧 key。这样可以减少切换期间的发信中断。这次的记录仍按实际发生的顺序写,不能把后来验证成功改写成提前验证过。

后续脚本只在进程内提取和传递新凭据,不打印其内容,也不把整份说明文件直接当作 token。新密钥没有写入聊天或工具日志。

配置保存下来,还要确认线上发信在用它

这版 Cloud Mail 的 Resend token 保存在应用设置里,D1 保存配置,KV 缓存配置。JWT 签名密钥才放在 Worker Secrets。轮换发送 key,要更新的是应用设置及其缓存。

切换前,我先用新凭据直接发了一封合成测试邮件,确认真实邮箱收到。然后通过正常的管理员设置接口保存新 token,由设置服务更新 D1 并刷新 KV。

最后在私有验证过程中比对了 D1、KV 和原来的本地凭据文件,三处值一致。其他域名的凭据保持原样,不相关的 Resend key 也没有改动。这次没有改 Cloud Mail 源码,也不需要重新部署 Worker。

仅仅看到设置保存成功,还不能确定后续发信已经用上新值。应用若继续读取旧缓存,页面上的保存提示不会替它发现问题。所以这轮又从 Cloud Mail 的正常发送入口发信,检查实际走到发件服务的结果。

要让测试邮件走到 Resend

Cloud Mail 有内部投递逻辑。如果收件人全部属于应用配置的本地域名,邮件可能直接在系统内投递。这样的收件成功,无法验收 Resend 密钥。

这次测试加入了外部收件人,使用 Resend 官方测试地址,并同时发送到自己的真实邮箱。随后检查应用保存的服务商邮件标识、Resend 的投递状态,以及邮箱里实际收到的来信。

结果是应用正常发送接口确实经过 Resend,服务商状态为 delivered,真实邮箱也收到了应用发出的邮件。那时旧 key 已经撤销,新 key 仍然可以完成这条发信流程。

旧凭据的检查也针对发送能力。直接用旧 key 请求发送接口,返回 HTTP 401;服务商的 key 列表里也已经没有旧 key。

只有发送权限的 key 可能本来就不能调用管理接口,因此管理接口报 401 不足以证明撤销成功。这次保留的证据是旧 key 的发送请求被拒绝,同时新 key 通过应用完成发信和实际收件。

验证结束后,原来的私有凭据文件权限收紧到 0600,轮换过程中生成的临时新密钥副本和旧密钥备份也清理了。测试邮件继续保留。文章只记录结果,不附完整指纹、请求头、账号标识或邮箱截图。

继续阅读

2026
  1. 做一只不催你的桌面宠物

    产品设计 · 桌面宠物 · macOS

2025
  1. 今天成为了 MooTool 贡献者

    Contributors · GitHub · Tools