你的邮件你做主:手把手打造永不绑定服务商的私人邮件中转站

概述

Cloudflare Email Routing 是 Cloudflare 提供的一项免费邮件路由服务。它让你可以在自定义域名下接收邮件,然后自动转发到你的任意私人邮箱(Gmail、QQ邮箱、Outlook 等)。免费额度对个人博客完全够用——不限转发量,只收域名本身的流量费(零成本)。


为什么选它,而不是私人邮箱

大多数人习惯直接用私人邮箱联系,但对拥有独立域名的开发者来说,Email Routing 有几个实实在在的好处:

  • 自定义域名 = 专业形象contact@yourdomain.comyourname@gmail.com 更像一个正经项目或品牌的联系地址。
  • 换服务商零成本。今天用 QQ 邮箱收信,明天想切到 Gmail——在 Cloudflare 后台改一个目标地址就行,不需要通知任何人。邮件地址永远是你的域名。
  • 与 Worker 无缝集成。这是最大的亮点——你可以在邮件到达目标邮箱之前,用 Worker 截获、解析、过滤、归档。本文的进阶部分会重点展开。
  • Catch-all 兜底。无论发信人把地址拼成什么,你都不会丢信。
  • 管理界面简洁。路由规则、目标地址、DNS 记录都在同一个页面,一目了然。

前置条件

你需要一个托管在 Cloudflare 的自定义域名,并已启用 Cloudflare 代理(橙色云)。


开始配置

DNS 自动配置

进入 Cloudflare Dashboard → 你的域名 → EmailEmail Routing,启用服务。

image.png

选择你的域名,Cloudflare 会自动添加以下三条 DNS 记录:

image.png

  • MX 记录:将邮件路由到 Cloudflare 的邮件服务器
  • SPF TXT 记录:授权 Cloudflare 代为转发邮件
  • DKIM TXT 记录:为转发邮件提供签名验证,降低被标记为垃圾邮件的概率

DNS 生效通常需要 5–15 分钟,最长不超过 24 小时。生效前 DNS 记录状态显示为"未设置"是正常的。

DNS records Locked ✅ Cloudflare 已自动配置 MX/SPF/DKIM 记录并锁定,防止误改,这是正常的保护机制

添加目标邮箱

在 Email Routing 页面切换到 Destination Addresses,添加你的真实邮箱地址,例如 your-personal@qq.com

配置路由

切换到 Routing rules,设置一条规则:将发往 @yourdomain.com 的邮件转发到刚才添加的目标地址。

image.png

开启 Catch-all

Catch-all 规则会捕获所有发送到你域名的邮件,包括拼写错误的地址。例如:别人误写成 ifno@yourdomain.com,Catch-all 仍能收到这封邮件。

激活后配置一个目标地址即可。

image.png

最终配置完成的效果:

image.png


测试

  1. 用另一个邮箱(不要和目标地址相同)发送一封测试邮件到 hello@yourdomain.com
  2. 检查目标邮箱是否收到转发邮件
  3. 如果没收到,检查垃圾邮件文件夹

例如我使用微软邮箱向自定义邮箱发送消息,最终 QQ 邮箱成功接收到了转发:

image.png


进阶:Worker 中转处理

基础的转发功能足以应付日常通信,但如果你对邮件有更进一步的需求——比如过滤垃圾邮件、解析内容、自动归档——就需要 Worker 上场了。

为什么需要 Worker

默认情况下,Email Routing 只做一件事:收到邮件 → 转发到目标邮箱。你不能在中间插入任何逻辑。

有了 Worker 作为中转层,你可以在邮件到达目标邮箱之前:

  • 检查发件人、主题、正文,决定放行还是拒收
  • 将正文解析后存入数据库(见下一节)
  • 根据自定义规则将不同邮件转发到不同地址
  • 甚至触发 Webhook 通知到其他服务

wrangler.json 配置

对应的 wrangler.json 配置:

{
  "name": "blog-api",
  "main": "src/index.ts",
  "compatibility_date": "2026-06-10",
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "blog-database",
      "migrations_dir": "migrations"
    }
  ]
}

不需要特殊的 send_email binding——只用 message.forward() 转发,email() 入口本身由 Email Routing 自动触发。

email() 入口

Worker 的 email() 函数会在每次收到邮件时自动触发,不需要 HTTP 路径,也不需要任何额外请求。这是 Email Routing 特有的入口函数。

export default {
  async email(message, env, ctx) {
    // message 是 ForwardableEmailMessage 对象
  }
};

简单的过滤示例

以下示例检查发件人或主题是否包含广告关键词,是则拒收,否则正常转发:

export default {
  async email(message, env, ctx) {
    const from = message.from;
    const subject = message.headers.get('subject') || '(无主题)';

    // 过滤垃圾邮件
    if (subject.includes('广告') || from.includes('spam.com')) {
      console.log('丢弃垃圾邮件');
      message.setReject('垃圾邮件已过滤');
      return; // 不转发
    }

    // 正常转发(保留原始发件人)
    await message.forward('your-email@gmail.com');
  }
};

一个容易被忽略的关键点:转发地址从配置中来

上面的示例硬编码了转发地址。更好的做法是把转发地址存在 D1 数据库(见下一节),通过一个简单的管理 API 动态修改,不用改一次地址就重新部署一次 Worker。


进阶:D1 自主归档

如果你和我一样,希望所有邮件完全由自己管理,不依赖任何第三方邮箱的历史记录、搜索或导出功能,那就用 D1 把邮件存下来。

emails 表:核心字段

CREATE TABLE emails (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  message_id  TEXT NOT NULL,       -- 原始 Message-ID(去重)
  from_addr   TEXT NOT NULL,       -- 发件人
  to_addr     TEXT NOT NULL,       -- 收件人(你的域名邮箱)
  forward_to  TEXT NOT NULL DEFAULT '',  -- 最终转发目标
  subject     TEXT DEFAULT '',
  text_body   TEXT DEFAULT '',
  html_body   TEXT DEFAULT '',
  headers     TEXT DEFAULT '{}',   -- 完整原始 headers(JSON)
  created_at  TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE UNIQUE INDEX idx_emails_message_id ON emails(message_id);
  • message_id 加了唯一索引,避免同一封邮件被重复归档
  • text_bodyhtml_body 分别存储纯文本和 HTML 版本
  • headers 以 JSON 形式保留完整的邮件头部,方便日后调试

settings 表:动态转发地址

CREATE TABLE settings (
  key   TEXT PRIMARY KEY,
  value TEXT NOT NULL
);

典型的用法是一行记录:('forward_email', 'your-personal@qq.com')

这样做的最大好处是:改转发地址只需调用一次 API,不需要改代码、不需要重新部署 Worker

Worker 中的处理流程

email() 入口中,结合 D1 的完整处理流程如下:

import PostalMime from "postal-mime";

export async function handleEmail(message, env, ctx) {
  // 1. 读取并解析原始邮件(注意:message.raw 是 ReadableStream,只能读一次)
  const raw = await new Response(message.raw).arrayBuffer();
  const parsed = await PostalMime.parse(raw);

  // 2. 从 D1 settings 表获取动态转发地址
  const row = await env.DB
    .prepare("SELECT value FROM settings WHERE key = ?")
    .bind("forward_email")
    .first();
  const forwardTo = row?.value;

  // 3. 归档到 D1(INSERT OR IGNORE 保证幂等)
  await env.DB.prepare(
    `INSERT OR IGNORE INTO emails (message_id, from_addr, to_addr, forward_to, subject, text_body, html_body, headers)
     VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
  ).bind(
    parsed.messageId, parsed.from?.address, message.to,
    forwardTo, parsed.subject, parsed.text, parsed.html,
    JSON.stringify(Object.fromEntries(message.headers))
  ).run();

  // 4. 原样转发(保留原始发件人和正文)
  await message.forward(forwardTo);
}

5 条管理 API

配合 HTTP 入口,可以暴露一组简单的 REST API 来管理邮件数据:

方法 路径 功能
GET /api/email/list 分页列表
GET /api/email/detail 查看单封邮件详情
DELETE /api/email/delete 删除邮件
GET /api/email/forward 查看当前转发目标
PUT /api/email/forward 更新转发目标(无需重新部署)
// PUT /api/email/forward 示例
export async function handleSetForward(request, env) {
  const body = await request.json(); // { address: "..." }
  await env.DB
    .prepare("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)")
    .bind("forward_email", body.address)
    .run();
  return new Response(JSON.stringify({ address: body.address }));
}

⚠️ 两个重要概念,不要混淆

message.forward() EMAIL.send()
发件人 保留原始发件人,收件人看到的 From 是写信人 发件人必须是你的域名地址
正文 原样转发,不能修改 可自定义内容和标题
附件 完整保留 可重制
是否需要额外配置 不需要 需启用 Email Sending 服务
适用场景 中转、归档、过滤(本文核心) 主动发送新邮件(通知、验证码等)

简单记忆:forward()邮递员,原封不动把信送到指定地址;send()写信人,以你的身份主动发信。


扩展:Worker 主动发信

除了接收和转发,Cloudflare 还提供了通过 Worker 主动发送邮件的功能,用于自动回复、评论通知、表单确认等场景。

不过这里有一个很多人踩过的坑——Cloudflare Worker 发信实际上有两套通道,它们的政策限制完全不同。

两条通道,分清楚

MailChannels(免费内置) Cloudflare Email Sending
配置方式 零配置,开箱即用 需在 Dashboard 启用并验证域名
免费额度 不限量,但有严格政策限制 Free 版 每天 2000 封
用途限制 严禁批量/营销/订阅类邮件 ⚠️ 定位为事务性邮件,也不建议做订阅推送
违规后果 封禁发信通道,影响域名信誉 账号层面限制

你的 Worker 默认走的其实是 MailChannels 通道(哪怕你没显式配置过它)。而 EMAIL.send() 绑定使用的是 Cloudflare Email Sending。

与私人邮箱的区别

Worker 主动发信 私人邮箱(Gmail/QQ)
发件人地址 必须是你的域名邮箱,需先验证 通常是服务商分配的地址
每日上限 Free 版 2000 封 通常 500–2000 封
送达率 依赖 SPF/DKIM 配置 服务商已有良好信誉
邮件头部 部分自定义受限 完整控制

那订阅推送(Newsletter)呢?

这是最容易被问到的需求:"我能不能用 Worker 给所有订阅者发新文章通知?"

技术上可以,但政策上不行。

MailChannels 的 Acceptable Use Policy(AUP)明确禁止发送"内容相似、一次发送给多个收件人的非事务性邮件"。新文章通知正好踩中这条红线——同样的内容,主动推送给全列表。

如果强行使用,后果包括:

  • MailChannels 封禁发信权限,连事务邮件也被关停
  • 可能影响你的域名信誉,导致正常邮件也被拒收

正确的做法:使用专业的 Newsletter 服务(MailerLite / Buttondown / Resend),配合 Cloudflare 完成域名验证(SPF/DKIM)。收件人看到的发件人仍然是
oreply@yourdomain.com,品牌统一,退订合规。

方案 免费额度 适合谁
MailerLite 1000 订阅者,月 12000 封 想少写代码,专注写作
Resend 每天 100 封 想在 Worker 里完全掌控发信逻辑
Buttondown 1000 订阅者 极简 Newsletter 工具

✅ 适用场景总结

  • Worker 主动发信(免费)→ 事务性邮件:评论通知、联系表单回执、密码重置、订单确认
  • 专业服务(免费层)→ 订阅推送:新文章通知、周报、Newsletter
  • 邮箱服务商 → 日常通信:收发私人邮件

结语

Cloudflare Email Routing 是一个极简但强大的工具。基础配置五分钟就能搞定,加上 Worker 和 D1 后,它变成了一个完全由你掌控的邮件系统——自动归档、动态路由、按需过滤,全部免费。

对于拥有个人域名的开发者来说,花一个下午搭好这套体系,从此你的联系地址就是 you@yourdomain.com,不受任何邮箱服务商的绑架。


🌐 欢迎关注我的其他平台:

📧 联系我:mail@lxpavilion.top

posted @ 2026-07-26 22:49  PC2005-cloud  阅读(2)  评论(0)    收藏  举报