
Cloudflare Email Routing API 实战指南Worker 运行时接口、SendEmail 绑定与 REST API 完整解析【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills本篇技术指南围绕 Cloudflare Email Routing 的三层 API 体系展开Worker 运行时 APIForwardableEmailMessage收件处理接口、SendEmail 绑定Worker 内发事务邮件、REST API以编程方式管理路由规则与目标地址。结合当前仓库skills/.curated/cloudflare-deploy/references/email-routing/的完整参考文档与可运行代码示例读者将掌握从接收邮件 → 程序化处理 → 转发/拒绝/存储到Worker 内发送事务邮件再到REST 管理路由的完整实战能力。一、总览Email Routing 的三层 API 体系Cloudflare Email Routing 允许为你的域名创建自定义邮箱地址并将邮件路由到已验证的目标地址。它免费、注重隐私Cloudflare 不存储、不读取邮件内容并且内置Email Workers支持以编程方式处理入站邮件。前提是你的域名使用 Cloudflare 作为权威 DNS 服务器详见 email-routing/README.md。从 API 角度Email Routing 的能力可划分为三层Internet → MX Records → Cloudflare Email Routing ├─ Routing Rules (Dashboard / REST API) └─ Email Worker (你的代码) ├─ Forward 到目标地址 ├─ Reject 并携带原因 ├─ 存入 R2/KV/D1 └─ 通过 SendEmail 发外发邮件Routing Rules路由规则基于模式匹配的转发通过 Dashboard 或 REST API 配置简单但能力有限Email Workers邮件 Worker自定义 TypeScript 处理器可访问完整邮件内容实现复杂逻辑、解析、存储与拒绝SendEmail Binding面向 Worker 的出站邮件 API仅用于事务邮件禁止营销/群发。下文将逐一深入这三层 API 的接口定义、类型签名与调用约束。二、Worker 运行时 API接收并处理入站邮件2.1 Email Handler 接口Worker 通过导出包含email方法的处理器来接收邮件。该方法的签名与普通fetch处理器对称interface ExportedHandlerEnv unknown { email?(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext): void | Promisevoid; }三个参数的含义参数类型说明messageForwardableEmailMessage入站邮件对象见下文包含信封地址、头部与原始 MIME 流envEnvWorker 绑定的环境对象KV/R2/D1/SendEmail 等见 bindings/api.mdctxExecutionContext提供ctx.waitUntil()执行后台任务一个最简处理器来源configuration.md// src/index.ts export default { async email(message, env, ctx) { await message.forward(destinationexample.com); } } satisfies ExportedHandler;2.2 ForwardableEmailMessage入站邮件主接口ForwardableEmailMessage是 Email Workers 处理入站邮件的核心对象其完整定义合并 email-routing/api.md 与 email-workers/api.md 中的声明如下interface ForwardableEmailMessage { readonly from: string; // 信封发送者SMTP MAIL FROM如 senderexample.com readonly to: string; // 信封收件人SMTP RCPT TO如 youyourdomain.com readonly headers: Headers; // Web 标准 Headers 对象Subject、From、To 等 readonly raw: ReadableStream; // 原始 MIME 消息流只能消费一次 readonly rawSize: number; // 消息总大小字节 setReject(reason: string): void; forward(rcptTo: string, headers?: Headers): Promisevoid; reply(message: EmailMessage): Promisevoid; // 2025 年 3 月新增 }属性速查表属性类型说明fromstring信封发送者MAIL FROM非头部 Fromtostring信封收件人RCPT TO非头部 ToheadersHeaders邮件头部Subject、From、To 等rawReadableStream原始 MIME 消息只能消费一次需先缓存到ArrayBufferrawSizenumber消息总大小字节可用于体积校验方法说明setReject(reason)以永久性 SMTP 5xx 错误拒绝邮件邮件不会投递发件人可能收到退信bounceforward(rcptTo, headers?)转发到已验证的目标地址可选地附带额外头部。注意转发时仅允许追加X-*自定义头部reply(message)向原始发件人回复要求入站邮件通过 DMARC 校验、每事件仅回复一次、发件域需为接收域且具备 DMARC/SPF/DKIM 配置。forward附带自定义头部的示例来源email-workers/api.mdawait message.forward(inboxexample.com); // 携带自定义头部 const h new Headers(); h.set(X-Processed-By, worker); await message.forward(inboxexample.com, h);2.3 Headers 对象与常用头部message.headers是 Web 标准的Headers对象可用get()/set()等标准方法访问// 访问头部 const subject message.headers.get(subject); const from message.headers.get(from); const messageId message.headers.get(message-id); // 检查垃圾邮件评分 const spamScore parseFloat(message.headers.get(x-cf-spamh-score) || 0); if (spamScore 5) { message.setReject(Spam detected); }常用头部一览头部用途subject邮件主题from发件人显示地址to收件人显示地址x-cf-spamh-scoreCloudflare 垃圾邮件评分可用于过滤message-id消息唯一标识可用于去重dkim-signatureDKIM 签名用于认证2.4 信封地址 vs 头部地址最关键的区别message.from/message.to是SMTP 信封地址路由与认证用而headers.get(from)/headers.get(to)是头部地址面向用户展示。二者经常不同// 信封地址路由、认证检查 message.from // bouncesender.com真实发件人 message.to // youyourdomain.com你的地址 // 头部地址展示、面向用户 message.headers.get(from) // Alice alicesender.com message.headers.get(to) // Bob youyourdomain.com使用信封地址的场景认证 / SPF 检查路由决策退信bounce处理。使用头部地址的场景面向用户的展示Reply-To 逻辑用户侧过滤。一个典型误用是在错误的地址上做过滤——例如用头部 From 做白名单判断。正确的做法是使用message.from信封地址做路由/认证判断仅把头部地址用于展示// 路由/认证信封地址 if (message.from trustedexample.com) { } // 展示头部地址 const display message.headers.get(from);三、SendEmail Binding从 Worker 发送事务邮件SendEmail 是面向出站事务邮件的 Worker 绑定与入站处理Email Workers配合可形成完整的邮件闭环如自动回复、订单通知。3.1 配置wrangler.jsonc在wrangler.jsonc中通过send_email数组声明绑定// wrangler.jsonc { send_email: [ { name: EMAIL } ] }四种绑定形态来源email-workers/api.md形态配置行为Type 1任意已验证地址{ name: EMAIL }可使用任意已验证地址发送Type 2单一目标{ name: LOGS, destination_address: logsexample.com }仅允许发往指定地址Type 3目标白名单{ name: TEAM, allowed_destination_addresses: [aex.com, bex.com] }仅允许发往列表内地址Type 4发件人白名单{ name: NOREPLY, allowed_sender_addresses: [noreplyex.com] }仅允许使用指定发件人3.2 TypeScript 类型interface Env { EMAIL: SendEmail; } interface SendEmail { send(message: EmailMessage): Promisevoid; } interface EmailMessage { from: string | { name?: string; email: string }; to: string | { name?: string; email: string } | Arraystring | { name?: string; email: string }; subject: string; text?: string; html?: string; headers?: Headers; reply_to?: string | { name?: string; email: string }; }要点from/to/reply_to均可传纯字符串地址或{ name, email }结构化对象to还支持数组实现群发同一封邮件的多个收件人。text与html二选一或同时提供作为纯文本/富文本内容。3.3 完整发送示例interface Env { EMAIL: SendEmail; } export default { async fetch(request, env, ctx): PromiseResponse { await env.EMAIL.send({ from: { name: Acme Corp, email: noreplyyourdomain.com }, to: [ { name: Alice, email: aliceexample.com }, bobexample.com ], subject: Your order #12345 has shipped, text: Track your package at: https://track.example.com/12345, html: pTrack your package at: a hrefhttps://track.example.com/12345View tracking/a/p, reply_to: { name: Support, email: supportyourdomain.com } }); return new Response(Email sent); } } satisfies ExportedHandlerEnv;3.4 SendEmail 约束务必遵守发件地址From必须属于已启用 Email Routing 的已验证域名流量限制仅限事务邮件禁止群发/营销邮件速率限制Free 套餐约 100 封/分钟付费套餐更高不支持附件改用托管文件的链接如存到 R2无 DKIM 控制权Cloudflare 自动为邮件签名。四、REST API 操作以编程方式管理路由当需要把 Email Routing 的配置纳入 CI/CD 或自动化运维时可直接调用 Cloudflare 的 REST API。4.1 认证与 Base URL所有请求基于https://api.cloudflare.com/client/v4使用 Bearer Token 认证curl -H Authorization: Bearer $API_TOKEN https://api.cloudflare.com/client/v4/...4.2 关键端点操作方法端点启用路由POST/zones/{zone_id}/email/routing/enable禁用路由POST/zones/{zone_id}/email/routing/disable列出规则GET/zones/{zone_id}/email/routing/rules创建规则POST/zones/{zone_id}/email/routing/rules验证目标地址POST/zones/{zone_id}/email/routing/addresses列出目标地址GET/zones/{zone_id}/email/routing/addresses其中{zone_id}为域名所属 Zone 的 ID。注意新目标地址创建后Cloudflare 会向该地址发送验证邮件必须点击邮件中的验证链接后地址才会生效可用GET /zones/{id}/email/routing/addresses查看验证状态。4.3 创建路由规则示例curl -X POST https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules \ -H Authorization: Bearer $API_TOKEN \ -H Content-Type: application/json \ -d { enabled: true, name: Forward sales, matchers: [{type: literal, field: to, value: salesyourdomain.com}], actions: [{type: forward, value: [alicecompany.com]}], priority: 0 }字段说明字段类型说明enabledboolean规则是否启用namestring规则名称便于管理识别matchersarray匹配条件{type: literal, field: to, value: ...}精确匹配收件人actionsarray执行动作{type: forward, value: [目标地址]}转发prioritynumber优先级数值越小越先执行Matcher 类型literal精确匹配与allcatch-all 兜底捕获。排障提示若规则不触发依次检查——优先级冲突确认 lowerfirst、matcher 是否精确匹配、catch-all 规则是否覆盖了精确规则、目标地址是否已验证。也可以在 Worker 部署后通过 REST API 将 Worker 挂接到路由见 configuration.mdcurl -X PUT https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/settings \ -H Authorization: Bearer $API_TOKEN \ -d {enabled: true, worker: email-worker}五、实战模式从收件到处理再到出站的完整链路5.1 基础项目骨架wrangler.jsonc来源configuration.md{ name: email-worker, main: src/index.ts, compatibility_date: 2025-01-01, send_email: [{ name: EMAIL }] }带存储绑定的形态可同时接入 KV/R2/D1 实现归档、元数据、审计日志{ name: email-processor, send_email: [{ name: EMAIL }], kv_namespaces: [{ binding: KV, id: abc123 }], r2_buckets: [{ binding: R2, bucket_name: emails }], d1_databases: [{ binding: DB, database_id: def456 }] }interface Env { EMAIL: SendEmail; KV: KVNamespace; R2: R2Bucket; DB: D1Database; }5.2 核心处理模式以下模式全部摘自 patterns.md可直接复制到 Worker 中使用。1白名单/黑名单过滤// Allowlist const allowed [userexample.com, trustedcorp.com]; if (!allowed.includes(message.from)) { message.setReject(Not allowed); return; } await message.forward(inboxcorp.com);2解析邮件正文与附件结合 postal-mimeimport PostalMime from postal-mime; export default { async email(message, env, ctx) { // CRITICAL: 立即消费流 const raw await message.raw.arrayBuffer(); const parser new PostalMime(); const email await parser.parse(raw); console.log({ subject: email.subject, text: email.text, html: email.html, from: email.from.address, attachments: email.attachments.length }); await message.forward(inboxcorp.com); } } satisfies ExportedHandler;postal-mimev2.7.x会将入站邮件解析为结构化对象from/to/cc/bcc、subject、messageId、inReplyTo、references、date、html、text以及attachments含filename、mimeType、content等字段详见 email-workers/api.md。3垃圾邮件过滤const score parseFloat(message.headers.get(x-cf-spamh-score) || 0); if (score 5) { message.setReject(Spam detected); return; } await message.forward(inboxcorp.com);4归档到 R2interface Env { R2: R2Bucket; } export default { async email(message, env, ctx) { const raw await message.raw.arrayBuffer(); const key ${new Date().toISOString()}-${message.from}.eml; await env.R2.put(key, raw, { httpMetadata: { contentType: message/rfc822 } }); await message.forward(inboxcorp.com); } } satisfies ExportedHandlerEnv;5元数据写入 KVimport PostalMime from postal-mime; interface Env { KV: KVNamespace; } export default { async email(message, env, ctx) { const raw await message.raw.arrayBuffer(); const parser new PostalMime(); const email await parser.parse(raw); const metadata { from: email.from.address, subject: email.subject, timestamp: new Date().toISOString(), size: raw.byteLength }; await env.KV.put(email:${Date.now()}, JSON.stringify(metadata)); await message.forward(inboxcorp.com); } } satisfies ExportedHandlerEnv;6按主题路由export default { async email(message, env, ctx) { const subject message.headers.get(subject)?.toLowerCase() || ; if (subject.includes([urgent])) { await message.forward(oncallcorp.com); } else if (subject.includes([billing])) { await message.forward(billingcorp.com); } else if (subject.includes([support])) { await message.forward(supportcorp.com); } else { await message.forward(generalcorp.com); } } } satisfies ExportedHandler;7自动回复入站 SendEmail 出站 KV 去重的完整闭环interface Env { EMAIL: SendEmail; REPLIED: KVNamespace; } export default { async email(message, env, ctx) { const msgId message.headers.get(message-id); if (msgId await env.REPLIED.get(msgId)) { await message.forward(archivecorp.com); return; } ctx.waitUntil((async () { await env.EMAIL.send({ from: noreplyyourdomain.com, to: message.from, subject: Re: (message.headers.get(subject) || ), text: Thank you. Well respond within 24h. }); if (msgId) await env.REPLIED.put(msgId, 1, { expirationTtl: 604800 }); })()); await message.forward(supportcorp.com); } } satisfies ExportedHandlerEnv;8提取附件到 R2import PostalMime from postal-mime; interface Env { ATTACHMENTS: R2Bucket; } export default { async email(message, env, ctx) { const parser new PostalMime(); const email await parser.parse(await message.raw.arrayBuffer()); for (const att of email.attachments) { const key ${Date.now()}-${att.filename}; await env.ATTACHMENTS.put(key, att.content, { httpMetadata: { contentType: att.mimeType } }); } await message.forward(inboxcorp.com); } } satisfies ExportedHandlerEnv;9写入 D1 审计日志import PostalMime from postal-mime; interface Env { DB: D1Database; } export default { async email(message, env, ctx) { const parser new PostalMime(); const email await parser.parse(await message.raw.arrayBuffer()); ctx.waitUntil( env.DB.prepare(INSERT INTO log (ts, from_addr, subj) VALUES (?, ?, ?)) .bind(new Date().toISOString(), email.from.address, email.subject || ) .run() ); await message.forward(inboxcorp.com); } } satisfies ExportedHandlerEnv;10多租户路由SaaS 场景interface Env { TENANTS: KVNamespace; } export default { async email(message, env, ctx) { const subdomain message.to.split()[1].split(.)[0]; const config await env.TENANTS.get(subdomain, json) as { forward: string } | null; if (!config) { message.setReject(Unknown tenant); return; } await message.forward(config.forward); } } satisfies ExportedHandlerEnv;5.3 模式选型速查模式适用场景存储Allowlist 白名单安全防护无Parse 解析正文/附件处理无Spam Filter 垃圾过滤降低垃圾邮件无R2 Archive 归档邮件存储R2KV Meta 元数据统计分析KVSubject Route 主题路由部门分拣无Auto-Reply 自动回复客服支持KVAttachments 附件提取文档管理R2D1 Log 审计日志合规审计D1Multi-Tenant 多租户SaaSKV六、关键陷阱、限制与调试gotchas 精华6.1 流只能消费一次最常见错误message.raw是ReadableStream只能消费一次。重复读取会报 stream already consumed 或导致 Worker 挂起// ❌ 错误 const email1 await parser.parse(await message.raw.arrayBuffer()); const email2 await parser.parse(await message.raw.arrayBuffer()); // 失败 // ✅ 正确先缓存为 ArrayBuffer const raw await message.raw.arrayBuffer(); const email await parser.parse(raw);最佳实践在任何异步操作之前立即消费message.raw。6.2 目标地址必须验证邮件无法转发的常见原因是目标地址未验证。解决添加目标地址 → 查看收件箱中的验证邮件 → 点击链接。验证状态可通过GET /zones/{id}/email/routing/addresses查询。6.3 发件认证SPF/DKIM/DMARC合法邮件被误拒通常是发件方域名缺少 SPF/DKIM/DMARC 记录。可在 Worker 中读取authentication-results头部判断认证结果const auth message.headers.get(authentication-results) || ; console.log({ spf: auth.includes(spfpass), dkim: auth.includes(dkimpass), dmarc: auth.includes(dmarcpass) }); if (!auth.includes(pass)) { message.setReject(Failed auth); return; }SPF 记录还应注意 DNS 查询次数转发会破坏 SPF且 lookup 次数超过 10 次会失败应精简include:项。6.4 缺头保护头部缺失时get()返回null直接调用方法会抛错// ❌ 错误 const subj message.headers.get(subject).toLowerCase(); // ✅ 正确 const subj message.headers.get(subject)?.toLowerCase() || ;6.5 资源限制资源FreePaid邮件大小25 MB25 MB路由规则数200200目标地址数200200Worker CPU 时间10ms50msSendEmail 速率~100/分钟更高CPU 时间超限的应对先在入口处按体积提前拒绝大邮件再把耗时工作放入ctx.waitUntil()const size parseInt(message.headers.get(content-length) || 0) / 1024 / 1024; if (size 20) { message.setReject(Too large); return; } ctx.waitUntil(expensiveWork()); await message.forward(destexample.com);6.6 本地调试与线上追踪本地开发wrangler dev提供__email特殊端点npx wrangler dev # 用 curl 模拟一封入站邮件 curl -X POST http://localhost:8787/__email \ --header content-type: message/rfc822 \ --data From: testexample.com To: youyourdomain.com Subject: Test Body生产环境日志npx wrangler tail建议在email处理器中用 try/catch 包裹业务逻辑任何异常都setReject返回给 SMTP 会话避免静默失败export default { async email(message, env, ctx) { try { console.log(From:, message.from); await process(message, env); } catch (err) { console.error(err); message.setReject(err.message); } } } satisfies ExportedHandler;6.7 八条最佳实践立即消费message.raw验证所有转发目标地址用可选链?.处理缺失头部路由判断使用信封地址message.from/message.to检查垃圾邮件评分头部先本地测试再部署用ctx.waitUntil执行后台工作尽早做体积检查。七、部署与类型安全补充部署npx wrangler deploy后在 DashboardEmail Email Routing 域名 Settings Email Workers选择目标 Worker或通过 REST API 挂接见上文 4.3DNS 记录启用 Email Routing 时 Cloudflare 自动创建MX记录isaac/linda/amir.mx.cloudflare.net与 SPF TXT 记录无需手工维护详见 configuration.mdTypeScript 支持安装cloudflare/workers-types后即可获得ForwardableEmailMessage、SendEmail等类型的完整补全与类型检查如需为绑定生成Env接口可运行npx wrangler types参见 bindings/api.md更多参考email-routing/README.md总览与决策树、patterns.md全部实战模式、gotchas.md完整排障清单、email-workers/api.mdreply()、EmailMessage构造器、postal-mime 与 mimetext 详细 API。【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考