cloudflare-email-routing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare Email Routing
Cloudflare Email Routing
Status: Production Ready ✅ | Last Verified: 2025-11-18
状态: 生产就绪 ✅ | 最后验证日期: 2025-11-18
What Is Email Routing?
什么是Email Routing?
Two capabilities:
- Email Workers - Receive and process incoming emails (allowlists, forwarding, parsing)
- Send Email - Send emails from Workers to verified addresses
Both free and work together for complete email functionality.
包含两大功能:
- Email Workers - 接收并处理邮件(白名单、转发、解析)
- 发送邮件 - 通过Workers向已验证地址发送邮件
两项功能均免费,可配合使用以实现完整的邮件功能。
Quick Start (10 Minutes)
快速入门(10分钟)
Part 1: Enable Email Routing
第一部分:启用Email Routing
Dashboard setup:
- Dashboard → Domain → Email → Email Routing
- Enable Email Routing → Add records and enable
- Create destination address:
- Custom:
hello@yourdomain.com - Destination: Your email
- Verify via email
- Custom:
- ✅ Basic forwarding active
控制台设置:
- 控制台 → 域名 → 邮件 → Email Routing
- 启用Email Routing → 添加记录并启用
- 创建目标地址:
- 自定义地址:
hello@yourdomain.com - 目标地址: 你的邮箱
- 通过邮件验证
- 自定义地址:
- ✅ 基础转发功能已激活
Part 2: Receiving Emails (Email Workers)
第二部分:接收邮件(Email Workers)
Install dependencies:
bash
bun add postal-mime@2.5.0 mimetext@3.0.27Create email worker:
typescript
// src/email.ts
import { EmailMessage } from 'cloudflare:email';
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());
console.log('From:', message.from);
console.log('Subject:', email.subject);
// Forward to destination
await message.forward('you@gmail.com');
}
};Configure wrangler.jsonc:
jsonc
{
"name": "email-worker",
"main": "src/email.ts",
"compatibility_date": "2025-10-11",
"node_compat": true // Required!
}Deploy and connect:
bash
bunx wrangler deployDashboard → Email Workers → Create address → Select worker
安装依赖:
bash
bun add postal-mime@2.5.0 mimetext@3.0.27创建邮件Worker:
typescript
// src/email.ts
import { EmailMessage } from 'cloudflare:email';
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());
console.log('发件人:', message.from);
console.log('主题:', email.subject);
// 转发至目标地址
await message.forward('you@gmail.com');
}
};配置wrangler.jsonc:
jsonc
{
"name": "email-worker",
"main": "src/email.ts",
"compatibility_date": "2025-10-11",
"node_compat": true // 必须配置!
}部署并关联:
bash
bunx wrangler deploy控制台 → Email Workers → 创建地址 → 选择Worker
Part 3: Sending Emails
第三部分:发送邮件
Add send email binding:
jsonc
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"send_email": [
{
"name": "SES",
"destination_address": "user@example.com"
}
]
}Send from worker:
typescript
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';
const msg = createMimeMessage();
msg.setSender({ name: 'App', addr: 'noreply@yourdomain.com' });
msg.setRecipient('user@example.com');
msg.setSubject('Hello!');
msg.addMessage({
contentType: 'text/plain',
data: 'Email body here'
});
const message = new EmailMessage(
'noreply@yourdomain.com',
'user@example.com',
msg.asRaw()
);
await env.SES.send(message);Load for complete walkthrough.
references/setup-guide.md添加发送邮件绑定:
jsonc
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"send_email": [
{
"name": "SES",
"destination_address": "user@example.com"
}
]
}通过Worker发送邮件:
typescript
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';
const msg = createMimeMessage();
msg.setSender({ name: 'App', addr: 'noreply@yourdomain.com' });
msg.setRecipient('user@example.com');
msg.setSubject('Hello!');
msg.addMessage({
contentType: 'text/plain',
data: 'Email body here'
});
const message = new EmailMessage(
'noreply@yourdomain.com',
'user@example.com',
msg.asRaw()
);
await env.SES.send(message);查阅 获取完整操作流程。
references/setup-guide.mdCritical Rules
重要规则
Always Do ✅
必须遵守 ✅
- Enable node_compat: true for postal-mime
- Verify destination addresses before sending
- Parse with postal-mime for email content
- Use mimetext for creating emails
- Check message.from for allowlists
- Forward with message.forward() (not manual)
- Handle errors (email delivery can fail)
- Test with real emails (not just dashboard)
- Add MX records (automatic via dashboard)
- Log email activity for debugging
- 启用node_compat: true 以支持postal-mime
- 发送前验证目标地址
- 使用postal-mime解析邮件内容
- 使用mimetext创建邮件
- 检查message.from以配置白名单
- 使用message.forward()转发邮件(不要手动转发)
- 处理错误(邮件投递可能失败)
- 使用真实邮件测试(不要仅依赖控制台)
- 添加MX记录(控制台会自动配置)
- 记录邮件活动以方便调试
Never Do ❌
切勿操作 ❌
- Never skip node_compat (postal-mime requires it)
- Never send without verification (delivery fails)
- Never hardcode email addresses in public code
- Never skip parsing (raw email is hard to work with)
- Never ignore spam (implement allowlists/blocklists)
- Never exceed Gmail limits (500 emails/day to Gmail)
- Never skip error handling (emails can fail)
- Never modify DNS manually (use dashboard)
- Never expose email content in logs (PII)
- Never assume instant delivery (email is async)
- 切勿跳过node_compat配置(postal-mime依赖该配置)
- 切勿向未验证地址发送邮件(投递会失败)
- 切勿在公开代码中硬编码邮箱地址
- 切勿跳过解析步骤(原始邮件难以处理)
- 切勿忽略垃圾邮件(实现白名单/黑名单)
- 切勿超出Gmail限制(每日向Gmail发送邮件最多500封)
- 切勿跳过错误处理(邮件可能投递失败)
- 切勿手动修改DNS(使用控制台操作)
- 切勿在日志中暴露邮件内容(属于个人可识别信息PII)
- 切勿假设邮件即时投递(邮件是异步的)
Common Patterns
常见模式
Allowlist
白名单
typescript
const allowlist = ['approved@domain.com'];
if (!allowlist.includes(message.from)) {
message.setReject('Not on allowlist');
return;
}
await message.forward('you@gmail.com');typescript
const allowlist = ['approved@domain.com'];
if (!allowlist.includes(message.from)) {
message.setReject('不在白名单内');
return;
}
await message.forward('you@gmail.com');Blocklist
黑名单
typescript
const blocklist = ['spam@bad.com'];
if (blocklist.includes(message.from)) {
message.setReject('Blocked');
return;
}
await message.forward('you@gmail.com');typescript
const blocklist = ['spam@bad.com'];
if (blocklist.includes(message.from)) {
message.setReject('已被拉黑');
return;
}
await message.forward('you@gmail.com');Reply to Email
回复邮件
typescript
const msg = createMimeMessage();
msg.setSender({ addr: 'noreply@yourdomain.com' });
msg.setRecipient(message.from);
msg.setSubject(`Re: ${email.subject}`);
msg.addMessage({
contentType: 'text/plain',
data: 'Thanks for your email!'
});
const reply = new EmailMessage(
'noreply@yourdomain.com',
message.from,
msg.asRaw()
);
await env.SES.send(reply);typescript
const msg = createMimeMessage();
msg.setSender({ addr: 'noreply@yourdomain.com' });
msg.setRecipient(message.from);
msg.setSubject(`Re: ${email.subject}`);
msg.addMessage({
contentType: 'text/plain',
data: '感谢您的来信!'
});
const reply = new EmailMessage(
'noreply@yourdomain.com',
message.from,
msg.asRaw()
);
await env.SES.send(reply);Parse Attachments
解析附件
typescript
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
for (const attachment of email.attachments) {
console.log('Filename:', attachment.filename);
console.log('Type:', attachment.mimeType);
console.log('Size:', attachment.content.byteLength);
}typescript
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
for (const attachment of email.attachments) {
console.log('文件名:', attachment.filename);
console.log('类型:', attachment.mimeType);
console.log('大小:', attachment.content.byteLength);
}Custom Routing Logic
自定义路由逻辑
typescript
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// Route based on subject
if (email.subject.includes('[Support]')) {
await message.forward('support@yourdomain.com');
} else if (email.subject.includes('[Sales]')) {
await message.forward('sales@yourdomain.com');
} else {
await message.forward('general@yourdomain.com');
}
}typescript
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// 根据主题路由
if (email.subject.includes('[Support]')) {
await message.forward('support@yourdomain.com');
} else if (email.subject.includes('[Sales]')) {
await message.forward('sales@yourdomain.com');
} else {
await message.forward('general@yourdomain.com');
}
}Email Message Properties
邮件消息属性
Incoming Messages (ForwardableEmailMessage)
收件消息(ForwardableEmailMessage)
typescript
message.from // Sender email
message.to // Recipient email
message.headers // Email headers
message.raw // Raw email stream
message.rawSize // Size in bytes
// Methods
message.forward(address) // Forward to address
message.setReject(reason) // Reject emailtypescript
message.from // 发件人邮箱
message.to // 收件人邮箱
message.headers // 邮件头
message.raw // 原始邮件流
message.rawSize // 字节大小
// 方法
message.forward(address) // 转发至指定地址
message.setReject(reason) // 拒绝邮件Parsed Email (PostalMime)
解析后的邮件(PostalMime)
typescript
email.from // { name, address }
email.to // [{ name, address }]
email.subject // Subject line
email.text // Plain text body
email.html // HTML body
email.attachments // Array of attachments
email.headers // All headerstypescript
email.from // { name, address }
email.to // [{ name, address }]
email.subject // 主题
email.text // 纯文本正文
email.html // HTML正文
email.attachments // 附件数组
email.headers // 所有邮件头Top 5 Errors Prevented
可预防的五大常见错误
- "Email Trigger not available": Enable node_compat: true
- Destination not verified: Verify all send destinations
- Gmail rate limit: Max 500 emails/day to Gmail
- SPF permerror: Use dashboard to configure DNS
- Worker call failed: Check logs for parsing errors
- "Email Trigger不可用": 启用node_compat: true
- 目标地址未验证: 验证所有发送目标地址
- Gmail速率限制: 每日向Gmail发送邮件最多500封
- SPF永久错误: 使用控制台配置DNS
- Worker调用失败: 检查日志排查解析错误
Use Cases
应用场景
Use Case 1: Support Ticket System
场景1:客服工单系统
typescript
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// Create ticket in database
await env.DB.prepare(
'INSERT INTO tickets (email, subject, body, created_at) VALUES (?, ?, ?, ?)'
).bind(message.from, email.subject, email.text, Date.now()).run();
// Send confirmation
const msg = createMimeMessage();
msg.setSender({ addr: 'support@yourdomain.com' });
msg.setRecipient(message.from);
msg.setSubject('Ticket Created');
msg.addMessage({
contentType: 'text/plain',
data: 'Your support ticket has been created.'
});
const confirmation = new EmailMessage(
'support@yourdomain.com',
message.from,
msg.asRaw()
);
await env.SES.send(confirmation);
}typescript
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// 在数据库中创建工单
await env.DB.prepare(
'INSERT INTO tickets (email, subject, body, created_at) VALUES (?, ?, ?, ?)'
).bind(message.from, email.subject, email.text, Date.now()).run();
// 发送确认邮件
const msg = createMimeMessage();
msg.setSender({ addr: 'support@yourdomain.com' });
msg.setRecipient(message.from);
msg.setSubject('工单已创建');
msg.addMessage({
contentType: 'text/plain',
data: '您的客服工单已创建。'
});
const confirmation = new EmailMessage(
'support@yourdomain.com',
message.from,
msg.asRaw()
);
await env.SES.send(confirmation);
}Use Case 2: Email Notifications
场景2:邮件通知
typescript
export default {
async fetch(request, env, ctx) {
// User signup
const { email, name } = await request.json();
const msg = createMimeMessage();
msg.setSender({ name: 'App', addr: 'noreply@yourdomain.com' });
msg.setRecipient(email);
msg.setSubject('Welcome!');
msg.addMessage({
contentType: 'text/html',
data: `<h1>Welcome, ${name}!</h1>`
});
const message = new EmailMessage(
'noreply@yourdomain.com',
email,
msg.asRaw()
);
await env.SES.send(message);
return new Response('Welcome email sent!');
}
};typescript
export default {
async fetch(request, env, ctx) {
// 用户注册
const { email, name } = await request.json();
const msg = createMimeMessage();
msg.setSender({ name: 'App', addr: 'noreply@yourdomain.com' });
msg.setRecipient(email);
msg.setSubject('欢迎!');
msg.addMessage({
contentType: 'text/html',
data: `<h1>Welcome, ${name}!</h1>`
});
const message = new EmailMessage(
'noreply@yourdomain.com',
email,
msg.asRaw()
);
await env.SES.send(message);
return new Response('欢迎邮件已发送!');
}
};Use Case 3: Email Forwarding with Filtering
场景3:带过滤功能的邮件转发
typescript
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// Filter spam keywords
const spamKeywords = ['viagra', 'lottery', 'prince'];
const isSpam = spamKeywords.some(keyword =>
email.subject.toLowerCase().includes(keyword) ||
email.text.toLowerCase().includes(keyword)
);
if (isSpam) {
message.setReject('Spam detected');
return;
}
await message.forward('you@gmail.com');
}typescript
async email(message, env, ctx) {
const parser = new PostalMime.default();
const email = await parser.parse(await new Response(message.raw).arrayBuffer());
// 过滤垃圾邮件关键词
const spamKeywords = ['viagra', 'lottery', 'prince'];
const isSpam = spamKeywords.some(keyword =>
email.subject.toLowerCase().includes(keyword) ||
email.text.toLowerCase().includes(keyword)
);
if (isSpam) {
message.setReject('检测到垃圾邮件');
return;
}
await message.forward('you@gmail.com');
}When to Load References
何时查阅参考文档
Load references/setup-guide.md
when:
references/setup-guide.md当以下情况时查阅 references/setup-guide.md
:
references/setup-guide.md- First-time Email Routing setup
- Configuring MX records
- Setting up email workers
- Configuring send email binding
- Complete walkthrough needed
- 首次配置Email Routing
- 配置MX记录
- 设置Email Workers
- 配置发送邮件绑定
- 需要完整操作流程
Using Bundled Resources
使用捆绑资源
References ():
references/- - Complete setup walkthrough (enabling routing, email workers, send email)
setup-guide.md - - All 8 documented errors with solutions and prevention
common-errors.md - - MX records, SPF, DKIM configuration guide
dns-setup.md - - Local testing and development patterns
local-development.md
Templates ():
templates/- - Basic email receiving worker
receive-basic.ts - - Email allowlist implementation
receive-allowlist.ts - - Email blocklist implementation
receive-blocklist.ts - - Auto-reply email worker
receive-reply.ts - - Basic send email example
send-basic.ts - - Notification email pattern
send-notification.ts - - Wrangler configuration for email routing
wrangler-email.jsonc
参考文档 ():
references/- - 完整设置流程(启用路由、Email Workers、发送邮件)
setup-guide.md - - 8种已记录错误的解决方案与预防措施
common-errors.md - - MX记录、SPF、DKIM配置指南
dns-setup.md - - 本地测试与开发模式
local-development.md
模板 ():
templates/- - 基础邮件接收Worker
receive-basic.ts - - 邮件白名单实现
receive-allowlist.ts - - 邮件黑名单实现
receive-blocklist.ts - - 自动回复邮件Worker
receive-reply.ts - - 基础发送邮件示例
send-basic.ts - - 通知邮件模式
send-notification.ts - - Email Routing的Wrangler配置
wrangler-email.jsonc
Official Documentation
官方文档
- Email Routing: https://developers.cloudflare.com/email-routing/
- Email Workers: https://developers.cloudflare.com/email-routing/email-workers/
- Send Email: https://developers.cloudflare.com/email-routing/email-workers/send-email-workers/
Questions? Issues?
- Check for complete setup
references/setup-guide.md - Verify node_compat: true in wrangler.jsonc
- Confirm destination addresses verified
- Check logs for errors
- Email Routing: https://developers.cloudflare.com/email-routing/
- Email Workers: https://developers.cloudflare.com/email-routing/email-workers/
- Send Email: https://developers.cloudflare.com/email-routing/email-workers/send-email-workers/
有疑问?遇到问题?
- 查阅 获取完整设置流程
references/setup-guide.md - 验证wrangler.jsonc中是否启用node_compat: true
- 确认目标地址已验证
- 检查日志排查错误