← 文章

2026.06.12

把邮箱整个搬进 Cloudflare:收发、存储、推送全自托管的实战记录

目录

你不仅可以拥有一个邮件地址,你还可以拥有整个自主托管的邮箱系统。

并且把这个基于cloudflare邮箱系统来源代码放到下面。

https://github.com/Coldplay-now/cf-mail

我把自己域名的邮箱从 Zoho 搬到了 Cloudflare 上。收信、发信、存邮件、读邮件的界面、手机来信提醒,全都在Cloudflare 账号里跑,没再依赖别人的邮箱服务。

我把「邮件」从一个需要专门客户端和协议的东西,变成了 agent 触手可及的一个 API + 一张数据库表 + 一个来信钩子——发得出、收得回、读得到、权限可控,agent 要的就这些。

AI时代,邮件的性质变了,我需要给我的agent智能体去创建一个跟外部的数据缓冲池。而邮箱这种载体是最好的一个选择。我的意思是说是时候去给自己的agent设计邮箱了。

折腾完一身轻松,把过程记下来,也给同样受够了免费邮箱限制的人一个参考。

为什么要折腾

我的域名邮箱本来挂在 Zoho 免费版。能用,但有几个地方一直膈应我:

免费版不给开 IMAP 和 POP,我只能在 Zoho 自己的网页和 App 里看邮件,想用别的客户端、或者把邮件接进自己的程序里,门都没有。能发信,但配额卡得紧,也谈不上自动化。

更别扭的是,我博客的文章、图片、项目数据早就全在自己的 Cloudflare 上了(D1 数据库加 R2 存储),就剩邮箱是块租来的地,住着总归不踏实。

自建邮箱的老路子是租台服务器装 Postfix、Dovecot,但那等于给自己找了份兼职运维,IP 信誉还得天天伺候,太重。

后来,我发现 Cloudflare 这两年其实把发邮件、收邮件的零件都凑齐了,拼一拼就能用,这才动了手。

整个系统长什么样

就是几样东西串起来:

收信,MX 记录指向 Cloudflare 的 Email Routing,它收到信之后不直接投递,而是交给我写的一个 Worker。

Worker 先查数据库里有没有这个收件地址,没有就当场退信;有就用 postal-mime 把邮件拆开,附件丢进 R2,正文和元信息写进 D1。需要的话再转发一份到我的常用外部邮箱。

发信这头,我自己的后台调一个发送接口,背后是 Cloudflare 的 Email Service,它替我签好 DKIM 发出去。

读信,我自己做的邮件客户端,收件箱、已发送、搜索、联系人这些该有的都有。

最让我觉得值的一点是:邮件一旦进了我自己的数据库,它就成了普通数据。

想按会话归类,写条 SQL;想全文搜索,再写条 SQL;想拉黑某个发件人,就是数据库里加一行。以前在 Zoho 里只能干等官方出功能的事,现在自己一晚上就写完了。

cf-mail 收件箱

收信:不认识的地址当场退回

Email Routing 负责最前面收信那一层,免费、不限量。我没有一个地址一个地址去配规则,而是设了一条「全部转给 Worker」,让 Worker 自己查数据库决定收不收。

这么做有两个好处。

第一,开通或停用一个邮箱地址,就是在后台加一行、改个开关,根本不用进 Cloudflare 控制台;那些拿字典乱撞地址的垃圾邮件,地址对不上就直接被退回,连存都不存。

第二,顺手就有了带后缀的地址:比如我注册某个网站时填 me+xx@mydomainexample.com,信照样进我的主邮箱,但哪天这个地址开始收垃圾邮件,我一看后缀就知道是谁把我卖了。

解析邮件用的是 postal-mime,纯 JavaScript 写的,能在 Worker 里跑。万一 Worker 出错崩了,发件方的邮件服务器会按邮件协议自动重发,所以最坏情况是信晚到一会儿,不会丢。

发信:那个所有人都会栽的坑

发信走 Cloudflare 的 Email Service(还在公测)。

这里有个坑:在控制台开通这个服务之后,一定要把 Worker 重新部署一次。因为发送通道是部署的时候才绑上去的。要是忘了重新部署,信会走一条旧的、没有 DKIM 签名的通道发出去——结果就是稳稳进对方的垃圾箱,而且控制台里「已发送」的计数一直是零。

还有个测试时容易自己骗自己的地方:迁移过程中我从旧邮箱给自己发测试信,两头都是同一家服务商,信根本没出公网就「成功」了,其实啥也没验证到。要测就拿一个完全不相干的邮箱(比如 Gmail)发过来。

读信:后台里顺手长出一个邮件客户端

读信的界面,我直接做进了邮箱后台。因为邮件就是数据库里的表,很多听起来高级的功能写起来其实平平无奇:

会话归类,是按邮件头里的引用关系把同一串对话聚到一起;搜索,就是在主题、地址、正文里做模糊匹配,对个人这点邮件量完全够用;联系人也不用单独维护,从来往记录里自动攒出来。

黑名单更简单,联系人表上加个「拉黑」标记,收信的 Worker 看到是被拉黑的人就直接标成垃圾,拉黑的时候还顺便把以前收到的也清一遍。

至于邮件里五花八门的 HTML 样式,我的做法是原样存下来,显示的时候先过一遍清洗,再塞进一个隔离的 iframe 里渲染——这样营销邮件的样式再花哨,也撑不破我的页面。

邮件详情

提醒:Worker 直接就能推到 iPhone

最后一块是手机来信提醒。网页这边用的是标准的 Web Push。但我写了个原生的 iOS 管理 App,得用苹果的 APNs 推送。

网上关于「Cloudflare Worker 到底能不能直连 APNs」说法很乱,因为 APNs 只认 HTTP/2,而 Worker 的出站请求支持得怎么样,文档讲得也含糊。

我实际试下来:能,直接就能用。 Worker 里签好令牌,发给苹果的推送服务器,几秒钟手机就弹出来了,中间不需要任何中转。

这里也有个能让你白白浪费半小时的细节:App Store Connect 那把 API 密钥不能拿来签 APNs 的令牌,会被拒。得专门去开发者后台另建一把 APNs 密钥。

我踩过的坑,一次性列给你

  • 开通发信服务后忘了重新部署 Worker:信没 DKIM、进垃圾箱、计数为零。重新部署一次就好。
  • 拿同一家服务商的两个邮箱互发来测试,无效,要用外部邮箱测。
  • 程序里用默认的 User-Agent 调接口:会被 Cloudflare 拦(报 1010)。带个自定义的 UA 就行。
  • 拿 App Store Connect 的密钥签 APNs:会报 InvalidProviderToken,得单独建 APNs 密钥。
  • 域名设了严格的 DMARC 之后还从别的渠道冒充本域名发信:会被全拒。所有发信都走签了 DKIM 的那条通道。
  • SPF 记录写了多条:校验会失败,合并成一条。

花了多少钱,又有哪些做不到

钱这块:收信免费,发信包含在 Workers 的付费套餐里(每月五美元,含三千封),对我个人绰绰有余。这个套餐我本来就为个人独立站开着,所以邮箱基本是白送的。

也得说清楚它的边界。Email Service 还在公测,一封信收件人最多五十个,目前发信还不支持附件(收信附件不限,走 R2)。

最大的限制还是没有 IMAP,第三方邮件客户端连不进来,看信只能用我自己的界面——这对我是好事(界面完全归我管),但你要是离不开 Apple Mail、Outlook 那套工作流,这条路就不适合你。

另外它现在是单人用的,多人多权限那套得自己再搭。

写在最后

折腾这一通,最大的收获其实不是省了订阅费,而是邮件从「别人家的服务」变成了「我自己系统里的一份子」。

来了新邮件可以触发我想要的任何动作,我写的小工具、甚至 AI 助手都能用同一个接口替我发通知,黑名单想怎么改就怎么改。

以前这些都得看服务商脸色,现在不过是一次提交的事。

如果你也在 Cloudflare 上跑着东西,又恰好烦透了免费邮箱的种种限制,这条路真的可以走:零件都是现成的,凑起来一个周末足够,剩下的就是慢慢打磨成自己顺手的样子。

本文所属主题:独立开发与自托管 枢纽 →

相关文章