关于 cloudflare-email-routing
面向 Cloudflare Workers 开发者的 Email Routing 实践指南,介绍收取、解析、转发和发送邮件的配置与代码模式。还覆盖常见部署与投递问题的预防和排查。
主要功能
- 01Email Workers 收取与处理邮件
- 02解析邮件内容和附件
- 03按规则过滤与转发
- 04Worker 发送邮件
- 05配置 DNS 与邮件绑定
适用场景
- 01将来信存入 D1 或 KV
- 02按主题路由到不同收件地址
- 03为支持请求发送自动回复
- 04从 Worker 发送通知邮件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill cloudflare-email-routing安装后在 Claude Code 中输入/cloudflare-email-routing确认已加载。
附带文件README.mdSKILL.json
详细介绍
Cloudflare Email Routing
状态:已可用于生产环境 ✅ 最后更新:2025-10-23 最新版本:[email protected],[email protected]
什么是 Cloudflare Email Routing?
Cloudflare Email Routing 提供两项互补功能:
- Email Workers - 使用自定义逻辑(允许列表、阻止列表、转发、解析、回复)接收和处理收到的邮件
- Send Email - 从 Workers 向已验证的目标地址发送邮件(通知、警报、确认信息)
这两项功能均为免费,并可协同工作,为 Cloudflare Workers 提供完整的电子邮件功能。
快速开始(10 分钟)
第 1 部分:启用 Email Routing(控制面板)
前提条件:域名必须使用 Cloudflare DNS
- 登录 Cloudflare 控制面板 → 选择你的域名
- 前往 Email > Email Routing
- 选择 Enable Email Routing → Add records and enable
- 这会自动向 DNS 添加 MX 记录、SPF 和 DKIM
- 创建目标地址:
- 自定义地址:
[email protected] - 目标地址:你的个人电子邮件地址(例如
[email protected]) - 通过电子邮件验证目标地址
- 自定义地址:
- ✅ 基本电子邮件转发现已启用
你刚刚完成的操作:配置了 DNS 和基本转发。现在,让我们为自定义逻辑添加 Workers。
第 2 部分:使用 Email Workers 接收邮件
1. 安装依赖项
npm install [email protected] [email protected]
为什么需要这些软件包:
postal-mime- 解析收到的邮件(标头、正文、附件)mimetext- 创建用于发送或回复的邮件
2. 创建 Email Worker
创建 src/email.ts:
import { EmailMessage } from 'cloudflare:email';
import PostalMime from 'postal-mime';
export default {
async email(message, env, ctx) {
// Parse the incoming message
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
console.log('From:', message.from);
console.log('To:', message.to);
console.log('Subject:', email.subject);
// Forward to verified destination
await message.forward('[email protected]');
},
};
3. 配置 Wrangler
更新 wrangler.jsonc:
{
"name": "email-worker",
"main": "src/email.ts",
"compatibility_date": "2025-10-11"
}
4. 部署并绑定
npx wrangler deploy
# In Cloudflare Dashboard:
# Email > Email Routing > Email Workers
# Select your worker → Create route → Enter address (e.g., [email protected])
你刚刚完成的操作:创建了一个用于记录并转发邮件的 Worker。
第 3 部分:从 Workers 发送邮件
1. 配置 Send Email 绑定
更新 wrangler.jsonc:
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"send_email": [
{
"name": "EMAIL",
"destination_address": "[email protected]"
}
]
}
重要:destination_address 必须是:
- 已启用 Email Routing 的域名
- Email Routing 设置中已验证的目标地址
2. 从 Worker 发送邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';
export default {
async fetch(request, env, ctx) {
// Create email message
const msg = createMimeMessage();
msg.setSender({ name: 'My App', addr: '[email protected]' });
msg.setRecipient('[email protected]');
msg.setSubject('Welcome to My App');
msg.addMessage({
contentType: 'text/plain',
data: 'Thank you for signing up!',
});
// Send via binding
const message = new EmailMessage(
'[email protected]',
'[email protected]',
msg.asRaw()
);
await env.EMAIL.send(message);
return new Response('Email sent!');
},
};
3. 部署
npx wrangler deploy
你刚刚完成的操作:将 Worker 配置为向已验证的地址发送邮件。
Email Workers:完整指南
运行时 API
EmailEvent 处理程序
export default {
async email(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) {
// Process email here
},
};
参数:
message- ForwardableEmailMessage 对象env- 环境绑定(KV、D1、机密等)ctx- 执行上下文(用于异步操作的 waitUntil)
ForwardableEmailMessage 属性
interface ForwardableEmailMessage {
readonly from: string; // Sender email
readonly to: string; // Recipient email
readonly headers: Headers; // Email headers
readonly raw: ReadableStream; // Raw email message
readonly rawSize: number; // Size in bytes
// Methods
setReject(reason: string): void;
forward(rcptTo: string, headers?: Headers): Promise<void>;
reply(message: EmailMessage): Promise<void>;
}
常见模式
模式 1:允许列表
仅接受来自获准发件人的邮件:
export default {
async email(message, env, ctx) {
const allowList = [
'[email protected]',
'[email protected]',
'[email protected]',
];
if (!allowList.includes(message.from)) {
message.setReject('Address not on allowlist');
return;
}
await message.forward('[email protected]');
},
};
适用场景:联系表单、私人电子邮件地址、团队收件箱
模式 2:阻止列表
拒绝来自特定发件人或域名的邮件:
export default {
async email(message, env, ctx) {
const blockList = [
'[email protected]',
'@suspicious-domain.com', // Block entire domain
];
const isBlocked = blockList.some(pattern =>
message.from.includes(pattern)
);
if (isBlocked) {
message.setReject('Sender blocked');
return;
}
await message.forward('[email protected]');
},
};
适用场景:垃圾邮件过滤、屏蔽骚扰者、按域名屏蔽
模式 3:解析并存储
提取邮件内容并存储到 D1 或 KV 中:
import PostalMime from 'postal-mime';
export default {
async email(message, env, ctx) {
// Parse email
const parser = new PostalMime.default();
const rawEmail = new Response(message.raw);
const email = await parser.parse(await rawEmail.arrayBuffer());
// Store in D1
await env.DB.prepare(
'INSERT INTO emails (from_addr, subject, text, received_at) VALUES (?, ?, ?, ?)'
).bind(
message.from,
email.subject,
email.text,
new Date().toISOString()
).run();
// Forward to inbox
await message.forward('[email protected]');
},
};
适用场景:邮件归档、工单系统、支持收件箱、审计日志
模式 4:自动回复
使用自定义逻辑发送自动回复:
import PostalMime from 'postal-mime';
import { createMimeMessage } from 'mimetext';
import { EmailMessage } from 'cloudflare:email';
export default {
async email(message, env, ctx) {
// Parse incoming email
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// Create reply
const msg = createMimeMessage();
msg.setSender({ name: 'Support Team', addr: '[email protected]' });
msg.setRecipient(message.from);
msg.setHeader('In-Reply-To', message.headers.get('Message-ID'));
msg.setSubject(`Re: ${email.subject}`);
msg.addMessage({
contentType: 'text/plain',
data: `Thank you for your message about "${email.subject}". We'll respond within 24 hours.`,
});
// Send reply
const replyMessage = new EmailMessage(
'[email protected]',
message.from,
msg.asRaw()
);
await message.reply(replyMessage);
// Also forward to team inbox
await message.forward('[email protected]');
},
};
适用场景:不在办公室时的自动回复、支持工单确认、自动化响应
模式 5:条件路由
根据邮件内容将邮件路由到不同目标地址:
import PostalMime from 'postal-mime';
export default {
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
const subject = email.subject.toLowerCase();
// Route based on subject keywords
if (subject.includes('urgent') || subject.includes('critical')) {
await message.forward('[email protected]');
} else if (subject.includes('invoice') || subject.includes('payment')) {
await message.forward('[email protected]');
} else if (subject.includes('support') || subject.includes('help')) {
await message.forward('[email protected]');
} else {
await message.forward('[email protected]');
}
},
};
适用场景:部门路由、优先级筛选、按类别划分的收件箱
Send Email:完整指南
配置
单个目标地址(简单)
{
"send_email": [
{
"name": "EMAIL",
"destination_address": "[email protected]"
}
]
}
行为:通过 env.EMAIL 发送的所有邮件都会发往此地址。
多个目标地址(灵活)
{
"send_email": [
{
"name": "EMAIL",
"allowed_destination_addresses": [
"[email protected]",
"[email protected]",
"[email protected]"
]
}
]
}
行为:可以向列表中的任意地址发送邮件。
多个绑定(便于管理)
{
"send_email": [
{
"name": "NOTIFICATIONS",
"destination_address": "[email protected]"
},
{
"name": "ALERTS",
"destination_address": "[email protected]"
}
]
}
行为:将不同绑定用于不同用途。
发送邮件
基本文本邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';
const msg = createMimeMessage();
msg.setSender({ name: 'My App', addr: '[email protected]' });
msg.setRecipient('[email protected]');
msg.setSubject('Welcome!');
msg.addMessage({
contentType: 'text/plain',
data: 'Welcome to our service!',
});
const email = new EmailMessage(
'[email protected]',
'[email protected]',
msg.asRaw()
);
await env.EMAIL.send(email);
HTML 邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';
const msg = createMimeMessage();
msg.setSender({ name: 'My App', addr: '[email protected]' });
msg.setRecipient('[email protected]');
msg.setSubject('Welcome!');
// Add both plain text and HTML versions
msg.addMessage({
contentType: 'text/plain',
data: 'Welcome to our service!',
});
msg.addMessage({
contentType: 'text/html',
data: '<h1>Welcome!</h1><p>Thanks for joining us.</p>',
});
const email = new EmailMessage(
'[email protected]',
'[email protected]',
msg.asRaw()
);
await env.EMAIL.send(email);
带自定义标头的邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';
const msg = createMimeMessage();
msg.setSender({ name: 'My App', addr: '[email protected]' });
msg.setRecipient('[email protected]');
msg.setSubject('Password Reset');
// Add custom headers
msg.setHeader('X-Priority', '1');
msg.setHeader('X-Application-ID', 'my-app-123');
msg.addMessage({
contentType: 'text/plain',
data: 'Click here to reset your password...',
});
const email = new EmailMessage(
'[email protected]',
'[email protected]',
msg.asRaw()
);
await env.EMAIL.send(email);
DNS 配置
自动设置(推荐)
在仪表板中启用 Email Routing 时,Cloudflare 会自动添加:
-
MX 记录 - 将邮件直接发送到 Cloudflare 的服务器
yourdomain.com. 300 IN MX 13 amir.mx.cloudflare.net. yourdomain.com. 300 IN MX 86 linda.mx.cloudflare.net. yourdomain.com. 300 IN MX 24 isaac.mx.cloudflare.net. -
SPF 记录 - 授权 Cloudflare 代表你发送邮件
yourdomain.com. 300 IN TXT "v=spf1 include:_spf.mx.cloudflare.net ~all" -
DKIM 记录 - 为外发邮件签名
Automatically configured per domain
手动设置(高级)
如果你需要从其他服务提供商迁移:
- 前往 Email > Email Routing > Settings
- 选择 Start disabling > Unlock records and continue
- 根据需要编辑 DNS 记录
- 准备就绪后,选择 Lock DNS records 以保护 Email Routing
警告:更改 MX 记录会导致 Email Routing 无法使用。仅在迁移服务提供商时执行此操作。
预防已知问题
此技能可预防 8 个已记录的问题:
问题 #1:“此 Workers 不支持 Email Trigger”
错误:测试邮件 Workers 时,失败并显示“此 Workers 不支持 Email Trigger”
原因:Wrangler dev 并不完全支持邮件触发器;必须通过已部署的 Workers 进行测试
预防措施:
- 测试前务必部署邮件 Workers
- 使用
wrangler tail进行实时调试 - 使用仪表板的“Test Email Event”功能(可用时)
问题 #2:目标地址验证错误
错误:已验证的目标地址在 Email Worker 转发中显示为“未验证”
来源:社区报告(Cloudflare Community)
原因:仪表板存在错误,只有在常规路由规则中也使用这些地址时,才会将其显示为已验证
预防措施:
- 先为每个目标地址创建常规转发规则
- 然后在 Email Workers 中使用相同的地址
- 部署 Workers 前验证地址
问题 #3:Gmail 速率限制
错误:“421:我们的系统检测到来自你 IP 地址的未经请求邮件发送速率异常”
来源:社区报告
原因:由于共享基础设施,Gmail 可能会将 Cloudflare 的 IP 范围标记为可疑
预防措施:
- 正确配置 SPF/DKIM/DMARC 记录
- 不要通过 Email Routing 发送批量邮件
- 大量发送时使用事务性邮件服务(例如 SendGrid、Mailgun)
- 限制发送速率(个人用途每小时最多发送 50-100 封邮件)
问题 #4:使用 MailChannels 时出现 SPF permerror
错误:通过 MailChannels 路由时出现 SPF permerror
来源:社区讨论
原因:通过多个服务转发时,SPF 记录链会中断
预防措施:
- 使用 Email Routing 的原生发送功能,而不是 MailChannels
- 如果使用 MailChannels,请正确配置 SPF include
- 使用 MXToolbox SPF checker 进行测试
问题 #5:免费套餐的日志记录有限
错误:无法查看 Workers 日志或邮件处理详情
来源:社区报告
原因:免费套餐的日志保留和流式传输功能有限
预防措施:
- 开发期间使用
wrangler tail查看实时日志 - 在邮件 Workers 中广泛使用
console.log() - 将关键数据存储在 D1/KV 中以便调试
- 升级到 Workers 付费套餐以获得更好的可观测性
问题 #6:活动日志不一致
错误:即使邮件已成功转发,在活动日志中仍显示为“已丢弃”
来源:社区报告
原因:仪表板错误地显示了状态
预防措施:
- 检查邮件是否实际送达,而不要只依赖仪表板
- 使用
wrangler tail验证处理情况 - 在 D1/KV 中实现自己的日志记录
- 使用真实邮件进行测试,确认邮件送达
问题 #7:测试邮件事件一直处于加载状态
错误:Dashboard 中的“Test Email Event”按钮一直处于加载状态
原因:Dashboard 测试界面存在 Bug(截至 2025-10 尚未解决)
预防措施:
- 不要依赖 Dashboard 测试功能
- 改用
curl配合本地开发(参见“本地开发”部分) - 部署后使用真实邮件进行测试
- 使用
wrangler tail监控处理情况
问题 #8:Worker 调用失败
错误:“Rejected reason: Unknown error: failed to call worker: Worker call failed for 3 times, aborting…”
来源:workers-sdk #9069,社区报告
原因:Worker 因运行时错误、超时或内存问题而崩溃
预防措施:
- 使用 try/catch 添加全面的错误处理
- 为外部 API 调用设置超时
- 在拒绝请求前将错误记录到 D1/KV
- 对非关键操作使用
ctx.waitUntil() - 使用各种邮件格式进行测试(纯文本、HTML、附件)
本地开发
接收邮件
Wrangler 通过 HTTP POST 模拟邮件接收:
# Start dev server
npx wrangler dev
# In another terminal, send test email
curl http://localhost:8787 -X POST \
--data-binary @- << EOF
From: [email protected]
To: [email protected]
Subject: Test Email
This is a test email body.
EOF
结果:Wrangler 会记录邮件处理过程,并显示转发邮件的目标位置。
发送邮件
Wrangler 会将已发送的邮件写入本地 .eml 文件:
// Your worker code
await env.EMAIL.send(message);
终端输出:
[wrangler:inf] send_email binding called with the following message:
/tmp/miniflare-abc123/files/email/message-123.eml
查看邮件:
cat /tmp/miniflare-abc123/files/email/message-123.eml
配置文件参考
完整的 wrangler.jsonc(接收 + 发送)
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "email-worker",
"main": "src/email.ts",
"account_id": "YOUR_ACCOUNT_ID",
"compatibility_date": "2025-10-11",
"observability": {
"enabled": true
},
// Send email binding
"send_email": [
{
"name": "NOTIFICATIONS",
"destination_address": "[email protected]"
},
{
"name": "ALERTS",
"allowed_destination_addresses": [
"[email protected]",
"[email protected]"
]
}
],
// Optional: Add other bindings
"d1_databases": [
{
"binding": "DB",
"database_name": "email-archive",
"database_id": "YOUR_DATABASE_ID"
}
],
"kv_namespaces": [
{
"binding": "EMAIL_CACHE",
"id": "YOUR_KV_ID"
}
]
}
TypeScript 类型
环境绑定
interface Env {
// Send email bindings
EMAIL: SendEmail;
NOTIFICATIONS: SendEmail;
ALERTS: SendEmail;
// Other bindings
DB: D1Database;
EMAIL_CACHE: KVNamespace;
}
interface SendEmail {
send(message: EmailMessage): Promise<void>;
}
完整类型定义
import { EmailMessage } from 'cloudflare:email';
interface ForwardableEmailMessage {
readonly from: string;
readonly to: string;
readonly headers: Headers;
readonly raw: ReadableStream;
readonly rawSize: number;
setReject(reason: string): void;
forward(rcptTo: string, headers?: Headers): Promise<void>;
reply(message: EmailMessage): Promise<void>;
}
declare module 'cloudflare:email' {
export class EmailMessage {
constructor(from: string, to: string, raw: string | ReadableStream);
}
}
完整设置检查清单
邮件路由设置
- 域名已添加到 Cloudflare DNS
- 已在 Dashboard 中启用 Email Routing
- 已自动添加 MX、SPF、DKIM 记录
- 至少有一个目标地址已通过验证
- 使用自定义地址测试基本转发功能
Email Workers(接收)
- 已安装
[email protected] - 已安装
[email protected] - 已创建包含
async email()处理程序的邮件 Worker - 已部署 Worker:
npx wrangler deploy - 已在 Dashboard 中将 Worker 绑定到邮件路由
- 向路由地址发送真实邮件进行测试
- 使用
wrangler tail验证日志
发送邮件
- 已在
wrangler.jsonc中配置send_email绑定 - 已指定
destination_address或allowed_destination_addresses - 所有目标地址都已在 Email Routing 中通过验证
- Worker 代码使用
env.EMAIL.send() - 已部署 Worker:
npx wrangler deploy - 通过 Worker 端点测试发送邮件
- 确认邮件已送达收件人
故障排除
问题:“Email Trigger not available to this workers”
解决方案:
- 部署 Worker:
npx wrangler deploy - 使用真实邮件进行测试,不要使用本地模拟
- 使用
wrangler tail监控处理情况
问题:“Destination address not verified”
解决方案:
- 在 Dashboard 中检查 Email Routing > Destination addresses
- 如有需要,点击“Resend verification”
- 先创建常规转发规则(作为 Bug 的变通方案)
- 在部署 Worker 前验证所有地址
问题:Gmail 因 421 错误拒收邮件
解决方案:
- 确认已配置 SPF/DKIM 记录(使用 Email Routing 时会自动配置)
- 降低发送频率(个人使用时每小时最多 50-100 封)
- 不要发送未经请求的邮件或群发邮件
- 高发送量场景可考虑使用事务性邮件服务
问题:邮件未从 Email Worker 转发
解决方案:
- 检查 Dashboard 中是否已将 Worker 绑定到正确的邮件路由
- 确认目标地址已通过验证
- 使用
wrangler tail查看处理日志 - 检查 Worker 代码中的错误(在 forward() 外围使用 try/catch)
- 确认 MX 记录仍指向 Cloudflare
问题:无法查看 Worker 日志
解决方案:
- 使用
wrangler tail --format pretty查看实时日志 - 在 Worker 中添加大量
console.log()语句 - 将调试信息存储在 D1 或 KV 中,以便稍后检查
- 升级到 Workers 付费计划,以延长日志保留时间
问题:Worker 因“failed to call worker”而崩溃
解决方案:
- 为所有操作添加 try/catch 错误处理
- 为外部 API 调用设置超时时间
- 使用不同的邮件格式(纯文本、HTML、附件)进行测试
- 检查 Worker 是否超出 CPU/内存限制
- 对非阻塞操作使用
ctx.waitUntil()
高级主题
解析邮件附件
import PostalMime from 'postal-mime';
export default {
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// Access attachments
if (email.attachments && email.attachments.length > 0) {
for (const attachment of email.attachments) {
console.log('Attachment:', attachment.filename);
console.log('Type:', attachment.mimeType);
console.log('Size:', attachment.content.length);
// Store in R2
await env.BUCKET.put(
`emails/${Date.now()}-${attachment.filename}`,
attachment.content
);
}
}
await message.forward('[email protected]');
},
};
基于邮件创建任务
import PostalMime from 'postal-mime';
export default {
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// Extract task from email subject
const taskMatch = email.subject.match(/\[TASK\](.*)/i);
if (taskMatch) {
const taskDescription = taskMatch[1].trim();
// Create task in D1
await env.DB.prepare(
'INSERT INTO tasks (description, created_by, created_at) VALUES (?, ?, ?)'
).bind(
taskDescription,
message.from,
new Date().toISOString()
).run();
// Send confirmation
await message.reply(new EmailMessage(
'[email protected]',
message.from,
`Task created: ${taskDescription}`
));
}
},
};
由邮件触发的工作流
export default {
async email(message, env, ctx) {
// Trigger Cloudflare Workflow based on email
if (message.from.endsWith('@trusted-domain.com')) {
await env.WORKFLOW.create({
params: {
emailFrom: message.from,
emailTo: message.to,
receivedAt: new Date().toISOString(),
},
});
}
await message.forward('[email protected]');
},
};
依赖项
必需:
[email protected]- 解析收到的邮件[email protected]- 创建要发送的邮件
内置:
cloudflare:email- EmailMessage 类(无需安装)
可选:
@cloudflare/workers-types- TypeScript 类型定义
官方文档
- Email Routing:https://developers.cloudflare.com/email-routing/
- Email Workers:https://developers.cloudflare.com/email-routing/email-workers/
- 发送邮件:https://developers.cloudflare.com/email-routing/email-workers/send-email-workers/
- 运行时 API:https://developers.cloudflare.com/email-routing/email-workers/runtime-api/
- 本地开发:https://developers.cloudflare.com/email-routing/email-workers/local-development/
- postal-mime:https://www.npmjs.com/package/postal-mime
- mimetext:https://www.npmjs.com/package/mimetext
软件包版本(已于 2025-10-23 验证)
{
"dependencies": {
"postal-mime": "^2.5.0",
"mimetext": "^3.0.27"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20251014.0",
"wrangler": "^4.44.0"
}
}
有疑问?遇到问题?
- 查看
references/common-errors.md,了解详细的故障排除信息 - 查看
references/dns-setup.md,获取 DNS 配置帮助 - 查看
references/local-development.md,了解测试模式 - 查看官方文档:https://developers.cloudflare.com/email-routing/
- 使用
wrangler tail进行实时调试 - 在控制面板中确认所有目标地址均已验证