[{"data":1,"prerenderedAt":2516},["ShallowReactive",2],{"post:\u002Fblog\u002Fcloudflare-email-service-send-receive-guide":3,"surroundings:\u002Fblog\u002Fcloudflare-email-service-send-receive-guide":2510},{"id":4,"title":5,"accent":6,"body":7,"description":2489,"draft":2490,"experience":2491,"extension":2492,"featured":417,"image":2493,"meta":2499,"navigation":417,"path":2500,"publishedAt":2501,"readingTime":682,"seo":2502,"stem":2503,"tags":2504,"updatedAt":2491,"__hash__":2509},"posts\u002Fblog\u002Fcloudflare-email-service-send-receive-guide.md","Cloudflare Email Service 收发实战：从 Email Routing 到 Workers、API 与 SMTP","cyan",{"type":8,"value":9,"toc":2456},"minimark",[10,19,22,34,56,63,66,72,77,80,136,139,149,152,158,165,171,175,178,183,186,189,211,215,218,221,235,238,242,245,248,251,262,265,273,280,297,300,311,314,320,323,327,330,348,351,358,368,756,766,860,876,880,886,1021,1024,1041,1045,1071,1074,1094,1097,1101,1104,1111,1181,1187,1190,1209,1215,1219,1430,1436,1440,1443,1761,1764,1770,1775,1779,1782,1835,1838,1841,1861,1864,1867,1989,1992,1995,1998,2001,2007,2010,2033,2040,2043,2046,2049,2055,2058,2064,2068,2071,2095,2098,2118,2121,2125,2136,2142,2145,2148,2169,2172,2233,2236,2239,2271,2275,2278,2298,2301,2318,2321,2324,2327,2330,2333,2353,2356,2359,2452],[11,12,13,14,18],"p",{},"很长一段时间里，Cloudflare 与邮件的关系主要停留在两件事上：管理 DNS，以及用 Email Routing 把 ",[15,16,17],"code",{},"support@example.com"," 转发到一个真实邮箱。",[11,20,21],{},"2026 年，这个边界明显向前移动了。",[11,23,24,25,29,30,33],{},"Cloudflare 在 4 月把 ",[26,27,28],"strong",{},"Email Sending"," 推进公开 Beta，让 Workers 可以直接调用 ",[15,31,32],{},"env.EMAIL.send()"," 发送事务邮件；6 月又加入了认证 SMTP。加上原有的 Email Routing 与 Email Workers，现在一套 Cloudflare Email Service 已经可以覆盖：",[35,36,37,41,44,47,50,53],"ul",{},[38,39,40],"li",{},"接收发往自有域名的邮件；",[38,42,43],{},"转发到经过验证的真实邮箱；",[38,45,46],{},"把来信交给 Worker 解析、过滤、入库或触发自动化；",[38,48,49],{},"在原邮件线程中回复；",[38,51,52],{},"从 Worker、REST API 或 SMTP 发送事务邮件；",[38,54,55],{},"查看发送日志、退信、抑制与投递指标。",[11,57,58,59,62],{},"这篇文章以 ",[26,60,61],{},"2026 年 7 月 28 日","的官方能力为准，梳理 Cloudflare 邮件收发的完整模型，并给出一套适合 Nuxt、Workers 与 D1 项目的实现思路。",[11,64,65],{},"先说结论：",[67,68,69],"blockquote",{},[11,70,71],{},"Cloudflare Email Service 不是传统的 IMAP \u002F POP3 邮箱，也不是让你在网页里收信的企业邮箱套件。它更像一组“邮件入口、发送通道和可编程事件”，适合验证码、密码重置、订单通知、系统告警、支持工单和邮件驱动的自动化。",[73,74,76],"h2",{"id":75},"先把接收和发送分开","先把“接收”和“发送”分开",[11,78,79],{},"邮件收发看起来是一件事，在 Cloudflare 里却是两条独立链路。",[81,82,83,102],"table",{},[84,85,86],"thead",{},[87,88,89,93,96,99],"tr",{},[90,91,92],"th",{},"方向",[90,94,95],{},"Cloudflare 产品",[90,97,98],{},"入口",[90,100,101],{},"Worker 能做什么",[103,104,105,120],"tbody",{},[87,106,107,111,114,117],{},[108,109,110],"td",{},"接收",[108,112,113],{},"Email Routing \u002F Email Workers",[108,115,116],{},"外部邮件服务器连接 Cloudflare MX",[108,118,119],{},"转发、回复、拒收、解析、写入 D1\u002FR2、投递 Queue",[87,121,122,125,127,130],{},[108,123,124],{},"发送",[108,126,28],{},[108,128,129],{},"Workers binding、REST API、认证 SMTP",[108,131,132,133],{},"发送文本\u002FHTML、附件、自定义头，获得 ",[15,134,135],{},"messageId",[11,137,138],{},"接收链路大致是：",[140,141,147],"pre",{"className":142,"code":144,"language":145,"meta":146},[143],"language-text","发送方 SMTP\n  → Cloudflare MX\n  → SPF \u002F DKIM \u002F DMARC 与信誉检查\n  → 匹配地址规则或 Catch-all\n  → 转发到邮箱 \u002F 交给 Worker \u002F 丢弃\n","text","",[15,148,144],{"__ignoreMap":146},[11,150,151],{},"发送链路则是：",[140,153,156],{"className":154,"code":155,"language":145,"meta":146},[143],"Nuxt API \u002F Worker \u002F 外部应用\n  → EMAIL binding \u002F REST API \u002F SMTP\n  → 发件域与参数校验\n  → Cloudflare 投递管线\n  → 收件方邮件服务器\n",[15,157,155],{"__ignoreMap":146},[11,159,160],{},[161,162],"img",{"alt":163,"src":164},"邮件在边缘入口完成检查后，被分发到真实邮箱、Worker 处理节点或拒绝路径","\u002Fmedia\u002Fposts\u002Fcloudflare-email-service-send-receive-guide\u002Finbound-routing-v1.webp",[11,166,167],{},[168,169,170],"em",{},"这是一张概念图。真实判断还包含 SMTP 会话、邮件认证、规则匹配和 Worker 执行限制。",[73,172,174],{"id":173},"_2026-年的产品边界与价格","2026 年的产品边界与价格",[11,176,177],{},"Cloudflare 当前把接收与发送放在同一个 Email Service 下，但两部分的可用计划不同。",[179,180,182],"h3",{"id":181},"email-routing接收与转发","Email Routing：接收与转发",[11,184,185],{},"Email Routing 在 Workers Free 和 Workers Paid 都可以使用。官方价格页把入站邮件数量标为不限量，但如果邮件被交给 Worker，仍然会消耗对应的 Worker 请求与计算资源。",[11,187,188],{},"它适合：",[35,190,191,198,201,208],{},[38,192,193,194,197],{},"把 ",[15,195,196],{},"support@yourdomain.com"," 转发到现有个人邮箱；",[38,199,200],{},"为不同网站创建独立的联系地址；",[38,202,203,204,207],{},"用 ",[15,205,206],{},"orders+shop-a@yourdomain.com"," 这样的子地址区分来源；",[38,209,210],{},"把来信交给 Worker 转换成工单、Webhook 或数据库记录。",[179,212,214],{"id":213},"email-sending事务邮件","Email Sending：事务邮件",[11,216,217],{},"Email Sending 当前仍是公开 Beta。",[11,219,220],{},"向任意收件人发送需要 Workers Paid 计划。按照 2026 年 7 月的官方价格：",[35,222,223,226,229,232],{},[38,224,225],{},"每个账户每月包含 3,000 封出站邮件；",[38,227,228],{},"超出后每 1,000 封为 0.35 美元；",[38,230,231],{},"发往账户内“已验证目标地址”的邮件始终免费，不计入包含额度；",[38,233,234],{},"邮件被 Email Service 接受后，即使随后发生硬退信，通常也会计入额度；在 API 边界直接拒绝的请求不会计入。",[11,236,237],{},"价格和 Beta 状态都可能继续调整，真正上线前应重新检查官方价格页，而不是把本文数字长期写死在预算模型里。",[73,239,241],{"id":240},"第一步正确接入域名","第一步：正确接入域名",[11,243,244],{},"Email Service 要求域名使用 Cloudflare DNS。发送与接收会创建两组用途不同的 DNS 记录。",[179,246,247],{"id":247},"接收邮件使用根域记录",[11,249,250],{},"启用 Email Routing 后，Cloudflare 会在域名根节点配置：",[35,252,253,256,259],{},[38,254,255],{},"MX：把来信送到 Cloudflare 的邮件入口；",[38,257,258],{},"SPF：授权 Cloudflare 转发邮件；",[38,260,261],{},"DKIM：为转发链路提供签名。",[11,263,264],{},"这些记录负责的是“别人发到你的域名”。",[179,266,268,269,272],{"id":267},"发送邮件使用-cf-bounce-记录","发送邮件使用 ",[15,270,271],{},"cf-bounce"," 记录",[11,274,275,276,279],{},"启用 Email Sending 时，Cloudflare 会为 ",[15,277,278],{},"cf-bounce.yourdomain.com"," 配置：",[35,281,282,285,288,291],{},[38,283,284],{},"MX：处理退信；",[38,286,287],{},"SPF：授权 Cloudflare 代域名发送；",[38,289,290],{},"DKIM：验证邮件内容未在途中被修改；",[38,292,293,296],{},[15,294,295],{},"_dmarc.yourdomain.com","：声明认证失败时的处理策略。",[11,298,299],{},"发送和接收使用不同的 DKIM selector。看到两条 DKIM 记录并不意味着重复配置。",[11,301,302,303,306,307,310],{},"如果域名已有 Google Workspace、Microsoft 365 或其他邮件服务，尤其要检查 MX 和 SPF 是否冲突。一个域名只能有一条有效 SPF 策略；需要同时授权多个服务时，应合并 ",[15,304,305],{},"include","，而不是再创建第二条 ",[15,308,309],{},"v=spf1","。",[11,312,313],{},"我的建议是把不同信誉用途拆到子域：",[140,315,318],{"className":316,"code":317,"language":145,"meta":146},[143],"noreply@example.com          账户验证码与密码重置\nnotify@mail.example.com      产品通知\nsupport@example.com          人工支持与来信处理\n",[15,319,317],{"__ignoreMap":146},[11,321,322],{},"事务通知、人工往来与未来可能出现的批量邮件互不拖累，故障和迁移边界也更清晰。",[73,324,326],{"id":325},"接收邮件从转发规则到-email-worker","接收邮件：从转发规则到 Email Worker",[11,328,329],{},"最简单的接收方式完全不用代码：",[331,332,333,336,342,345],"ol",{},[38,334,335],{},"在 Email Routing 中添加一个经过验证的目标邮箱；",[38,337,338,339,341],{},"创建 ",[15,340,196],{},"；",[38,343,344],{},"选择“发送到邮箱”；",[38,346,347],{},"来信会被 Cloudflare 转发到目标地址。",[11,349,350],{},"如果需要按主题分类、提取附件、建立工单或自动回复，就把动作改为“发送到 Worker”。",[179,352,354,357],{"id":353},"email-是入站邮件入口",[15,355,356],{},"email()"," 是入站邮件入口",[11,359,360,361,364,365,367],{},"Email Worker 的入口不是 ",[15,362,363],{},"fetch()","，而是 ",[15,366,356],{},"：",[140,369,373],{"className":370,"code":371,"language":372,"meta":146,"style":146},"language-ts shiki shiki-themes github-dark github-dark","interface Env {\n  SUPPORT_FORWARD_TO: string\n}\n\nexport default {\n  async email(message, env): Promise\u003Cvoid> {\n    const recipient = message.to.toLowerCase()\n    const subject = message.headers.get('subject') ?? ''\n\n    if (message.rawSize > 10 * 1024 * 1024) {\n      message.setReject('Message is too large for this mailbox')\n      return\n    }\n\n    if (recipient === 'support@example.com') {\n      console.log('Support email accepted', {\n        fromDomain: message.from.split('@').at(-1),\n        subjectLength: subject.length,\n        rawSize: message.rawSize,\n      })\n\n      await message.forward(env.SUPPORT_FORWARD_TO)\n      return\n    }\n\n    message.setReject('Unknown recipient')\n  },\n} satisfies ExportedHandler\u003CEnv>\n","ts",[15,374,375,392,406,412,419,430,468,489,520,525,553,570,576,582,587,603,620,651,663,669,675,680,700,705,710,715,730,736],{"__ignoreMap":146},[376,377,380,384,388],"span",{"class":378,"line":379},"line",1,[376,381,383],{"class":382},"sOPea","interface",[376,385,387],{"class":386},"sFR8T"," Env",[376,389,391],{"class":390},"suv1-"," {\n",[376,393,395,399,402],{"class":378,"line":394},2,[376,396,398],{"class":397},"s-3mD","  SUPPORT_FORWARD_TO",[376,400,401],{"class":382},":",[376,403,405],{"class":404},"s8ozJ"," string\n",[376,407,409],{"class":378,"line":408},3,[376,410,411],{"class":390},"}\n",[376,413,415],{"class":378,"line":414},4,[376,416,418],{"emptyLinePlaceholder":417},true,"\n",[376,420,422,425,428],{"class":378,"line":421},5,[376,423,424],{"class":382},"export",[376,426,427],{"class":382}," default",[376,429,391],{"class":390},[376,431,433,436,439,442,445,448,451,454,456,459,462,465],{"class":378,"line":432},6,[376,434,435],{"class":382},"  async",[376,437,438],{"class":386}," email",[376,440,441],{"class":390},"(",[376,443,444],{"class":397},"message",[376,446,447],{"class":390},", ",[376,449,450],{"class":397},"env",[376,452,453],{"class":390},")",[376,455,401],{"class":382},[376,457,458],{"class":386}," Promise",[376,460,461],{"class":390},"\u003C",[376,463,464],{"class":404},"void",[376,466,467],{"class":390},"> {\n",[376,469,471,474,477,480,483,486],{"class":378,"line":470},7,[376,472,473],{"class":382},"    const",[376,475,476],{"class":404}," recipient",[376,478,479],{"class":382}," =",[376,481,482],{"class":390}," message.to.",[376,484,485],{"class":386},"toLowerCase",[376,487,488],{"class":390},"()\n",[376,490,492,494,497,499,502,505,507,511,514,517],{"class":378,"line":491},8,[376,493,473],{"class":382},[376,495,496],{"class":404}," subject",[376,498,479],{"class":382},[376,500,501],{"class":390}," message.headers.",[376,503,504],{"class":386},"get",[376,506,441],{"class":390},[376,508,510],{"class":509},"s4wv1","'subject'",[376,512,513],{"class":390},") ",[376,515,516],{"class":382},"??",[376,518,519],{"class":509}," ''\n",[376,521,523],{"class":378,"line":522},9,[376,524,418],{"emptyLinePlaceholder":417},[376,526,528,531,534,537,540,543,546,548,550],{"class":378,"line":527},10,[376,529,530],{"class":382},"    if",[376,532,533],{"class":390}," (message.rawSize ",[376,535,536],{"class":382},">",[376,538,539],{"class":404}," 10",[376,541,542],{"class":382}," *",[376,544,545],{"class":404}," 1024",[376,547,542],{"class":382},[376,549,545],{"class":404},[376,551,552],{"class":390},") {\n",[376,554,556,559,562,564,567],{"class":378,"line":555},11,[376,557,558],{"class":390},"      message.",[376,560,561],{"class":386},"setReject",[376,563,441],{"class":390},[376,565,566],{"class":509},"'Message is too large for this mailbox'",[376,568,569],{"class":390},")\n",[376,571,573],{"class":378,"line":572},12,[376,574,575],{"class":382},"      return\n",[376,577,579],{"class":378,"line":578},13,[376,580,581],{"class":390},"    }\n",[376,583,585],{"class":378,"line":584},14,[376,586,418],{"emptyLinePlaceholder":417},[376,588,590,592,595,598,601],{"class":378,"line":589},15,[376,591,530],{"class":382},[376,593,594],{"class":390}," (recipient ",[376,596,597],{"class":382},"===",[376,599,600],{"class":509}," 'support@example.com'",[376,602,552],{"class":390},[376,604,606,609,612,614,617],{"class":378,"line":605},16,[376,607,608],{"class":390},"      console.",[376,610,611],{"class":386},"log",[376,613,441],{"class":390},[376,615,616],{"class":509},"'Support email accepted'",[376,618,619],{"class":390},", {\n",[376,621,623,626,629,631,634,637,640,642,645,648],{"class":378,"line":622},17,[376,624,625],{"class":390},"        fromDomain: message.from.",[376,627,628],{"class":386},"split",[376,630,441],{"class":390},[376,632,633],{"class":509},"'@'",[376,635,636],{"class":390},").",[376,638,639],{"class":386},"at",[376,641,441],{"class":390},[376,643,644],{"class":382},"-",[376,646,647],{"class":404},"1",[376,649,650],{"class":390},"),\n",[376,652,654,657,660],{"class":378,"line":653},18,[376,655,656],{"class":390},"        subjectLength: subject.",[376,658,659],{"class":404},"length",[376,661,662],{"class":390},",\n",[376,664,666],{"class":378,"line":665},19,[376,667,668],{"class":390},"        rawSize: message.rawSize,\n",[376,670,672],{"class":378,"line":671},20,[376,673,674],{"class":390},"      })\n",[376,676,678],{"class":378,"line":677},21,[376,679,418],{"emptyLinePlaceholder":417},[376,681,683,686,689,692,695,698],{"class":378,"line":682},22,[376,684,685],{"class":382},"      await",[376,687,688],{"class":390}," message.",[376,690,691],{"class":386},"forward",[376,693,694],{"class":390},"(env.",[376,696,697],{"class":404},"SUPPORT_FORWARD_TO",[376,699,569],{"class":390},[376,701,703],{"class":378,"line":702},23,[376,704,575],{"class":382},[376,706,708],{"class":378,"line":707},24,[376,709,581],{"class":390},[376,711,713],{"class":378,"line":712},25,[376,714,418],{"emptyLinePlaceholder":417},[376,716,718,721,723,725,728],{"class":378,"line":717},26,[376,719,720],{"class":390},"    message.",[376,722,561],{"class":386},[376,724,441],{"class":390},[376,726,727],{"class":509},"'Unknown recipient'",[376,729,569],{"class":390},[376,731,733],{"class":378,"line":732},27,[376,734,735],{"class":390},"  },\n",[376,737,739,742,745,748,750,753],{"class":378,"line":738},28,[376,740,741],{"class":390},"} ",[376,743,744],{"class":382},"satisfies",[376,746,747],{"class":386}," ExportedHandler",[376,749,461],{"class":390},[376,751,752],{"class":386},"Env",[376,754,755],{"class":390},">\n",[11,757,758,759,761,762,765],{},"其中 ",[15,760,444],{}," 是 ",[15,763,764],{},"ForwardableEmailMessage","，常用字段和操作包括：",[81,767,768,778],{},[84,769,770],{},[87,771,772,775],{},[90,773,774],{},"能力",[90,776,777],{},"用途",[103,779,780,790,800,810,820,830,840,850],{},[87,781,782,787],{},[108,783,784],{},[15,785,786],{},"message.from",[108,788,789],{},"SMTP envelope 的发件人",[87,791,792,797],{},[108,793,794],{},[15,795,796],{},"message.to",[108,798,799],{},"SMTP envelope 的收件人",[87,801,802,807],{},[108,803,804],{},[15,805,806],{},"message.headers",[108,808,809],{},"读取 Subject、Message-ID 等邮件头",[87,811,812,817],{},[108,813,814],{},[15,815,816],{},"message.raw",[108,818,819],{},"原始 MIME 数据流",[87,821,822,827],{},[108,823,824],{},[15,825,826],{},"message.rawSize",[108,828,829],{},"原始邮件大小",[87,831,832,837],{},[108,833,834],{},[15,835,836],{},"message.forward(address)",[108,838,839],{},"转发到已验证目标地址",[87,841,842,847],{},[108,843,844],{},[15,845,846],{},"message.reply(email)",[108,848,849],{},"在约束范围内回复原发件人",[87,851,852,857],{},[108,853,854],{},[15,855,856],{},"message.setReject(reason)",[108,858,859],{},"返回永久 SMTP 拒收",[11,861,862,863,865,866,868,869,865,872,875],{},"这里有一个容易忽略的安全点：",[15,864,786],{}," \u002F ",[15,867,796],{}," 是 SMTP envelope，邮件正文头部中的 ",[15,870,871],{},"From:",[15,873,874],{},"To:"," 则可能是另一组值。做授权、路由与审计时，不要把显示给用户看的头部地址误当成可信身份。",[179,877,879],{"id":878},"需要正文时再解析-mime","需要正文时再解析 MIME",[11,881,882,883,367],{},"只做转发或按地址路由时，没有必要完整解析邮件。要提取正文和附件，可以使用 ",[15,884,885],{},"postal-mime",[140,887,889],{"className":370,"code":888,"language":372,"meta":146,"style":146},"import PostalMime from 'postal-mime'\n\nexport default {\n  async email(message): Promise\u003Cvoid> {\n    const parsed = await PostalMime.parse(message.raw)\n\n    console.log({\n      subject: parsed.subject,\n      textLength: parsed.text?.length ?? 0,\n      attachmentCount: parsed.attachments.length,\n    })\n  },\n} satisfies ExportedHandler\n",[15,890,891,905,909,917,939,960,964,974,979,994,1003,1008,1012],{"__ignoreMap":146},[376,892,893,896,899,902],{"class":378,"line":379},[376,894,895],{"class":382},"import",[376,897,898],{"class":390}," PostalMime ",[376,900,901],{"class":382},"from",[376,903,904],{"class":509}," 'postal-mime'\n",[376,906,907],{"class":378,"line":394},[376,908,418],{"emptyLinePlaceholder":417},[376,910,911,913,915],{"class":378,"line":408},[376,912,424],{"class":382},[376,914,427],{"class":382},[376,916,391],{"class":390},[376,918,919,921,923,925,927,929,931,933,935,937],{"class":378,"line":414},[376,920,435],{"class":382},[376,922,438],{"class":386},[376,924,441],{"class":390},[376,926,444],{"class":397},[376,928,453],{"class":390},[376,930,401],{"class":382},[376,932,458],{"class":386},[376,934,461],{"class":390},[376,936,464],{"class":404},[376,938,467],{"class":390},[376,940,941,943,946,948,951,954,957],{"class":378,"line":421},[376,942,473],{"class":382},[376,944,945],{"class":404}," parsed",[376,947,479],{"class":382},[376,949,950],{"class":382}," await",[376,952,953],{"class":390}," PostalMime.",[376,955,956],{"class":386},"parse",[376,958,959],{"class":390},"(message.raw)\n",[376,961,962],{"class":378,"line":432},[376,963,418],{"emptyLinePlaceholder":417},[376,965,966,969,971],{"class":378,"line":470},[376,967,968],{"class":390},"    console.",[376,970,611],{"class":386},[376,972,973],{"class":390},"({\n",[376,975,976],{"class":378,"line":491},[376,977,978],{"class":390},"      subject: parsed.subject,\n",[376,980,981,984,986,989,992],{"class":378,"line":522},[376,982,983],{"class":390},"      textLength: parsed.text?.",[376,985,659],{"class":404},[376,987,988],{"class":382}," ??",[376,990,991],{"class":404}," 0",[376,993,662],{"class":390},[376,995,996,999,1001],{"class":378,"line":527},[376,997,998],{"class":390},"      attachmentCount: parsed.attachments.",[376,1000,659],{"class":404},[376,1002,662],{"class":390},[376,1004,1005],{"class":378,"line":555},[376,1006,1007],{"class":390},"    })\n",[376,1009,1010],{"class":378,"line":572},[376,1011,735],{"class":390},[376,1013,1014,1016,1018],{"class":378,"line":578},[376,1015,741],{"class":390},[376,1017,744],{"class":382},[376,1019,1020],{"class":386}," ExportedHandler\n",[11,1022,1023],{},"解析后的 HTML、文件名、Content-Type 与附件都来自外部输入。正确做法是：",[35,1025,1026,1029,1032,1035,1038],{},[38,1027,1028],{},"不在管理页面直接插入来信 HTML；",[38,1030,1031],{},"限制正文、附件数量和解压后大小；",[38,1033,1034],{},"文件写入 R2 前重新生成对象键，不使用原始文件名作为路径；",[38,1036,1037],{},"不让来信内容直接组成 SQL、Shell、模板或 AI 工具参数；",[38,1039,1040],{},"大任务交给 Queue，不要把杀毒、OCR、模型调用全塞进入站事件的同步 CPU 时间。",[179,1042,1044],{"id":1043},"转发回复拒绝与静默丢弃的区别","转发、回复、拒绝与静默丢弃的区别",[35,1046,1047,1053,1059,1065],{},[38,1048,1049,1052],{},[26,1050,1051],{},"转发","：适合人工邮箱继续处理，目标地址必须提前验证；",[38,1054,1055,1058],{},[26,1056,1057],{},"回复","：适合自动确认或邮件机器人，但有额外限制；",[38,1060,1061,1064],{},[26,1062,1063],{},"拒绝","：在 SMTP 阶段明确告诉发送方没有接收；",[38,1066,1067,1070],{},[26,1068,1069],{},"丢弃","：规则匹配后不继续处理，也不给发送方业务层反馈。",[11,1072,1073],{},"自动回复不能被当成任意发送接口。官方当前要求：",[35,1075,1076,1079,1082,1085,1088],{},[38,1077,1078],{},"来信必须有有效 DMARC 结果；",[38,1080,1081],{},"每个入站事件只能调用一次回复；",[38,1083,1084],{},"回复收件人必须是原始来信的发件人；",[38,1086,1087],{},"回复使用的发件域必须与接收邮件的域一致；",[38,1089,1090,1093],{},[15,1091,1092],{},"References"," 超过 100 项时会拒绝回复，以避免邮件环路。",[11,1095,1096],{},"这组限制反而是一件好事：它让“回复这封邮件”和“向任意地址发一封新邮件”成为两种不同权限。",[73,1098,1100],{"id":1099},"发送邮件优先使用-workers-binding","发送邮件：优先使用 Workers binding",[11,1102,1103],{},"如果应用本身运行在 Cloudflare Workers，Workers binding 是最自然的选择。它不需要在业务代码里保存 Cloudflare API Token。",[11,1105,1106,1107,1110],{},"先在 ",[15,1108,1109],{},"wrangler.jsonc"," 中声明绑定：",[140,1112,1116],{"className":1113,"code":1114,"language":1115,"meta":146,"style":146},"language-jsonc shiki shiki-themes github-dark github-dark","{\n  \"send_email\": [\n    {\n      \"name\": \"EMAIL\",\n      \"allowed_sender_addresses\": [\n        \"noreply@example.com\"\n      ]\n    }\n  ],\n  \"vars\": {\n    \"EMAIL_FROM\": \"noreply@example.com\"\n  }\n}\n","jsonc",[15,1117,1118,1123,1128,1133,1138,1143,1148,1153,1157,1162,1167,1172,1177],{"__ignoreMap":146},[376,1119,1120],{"class":378,"line":379},[376,1121,1122],{},"{\n",[376,1124,1125],{"class":378,"line":394},[376,1126,1127],{},"  \"send_email\": [\n",[376,1129,1130],{"class":378,"line":408},[376,1131,1132],{},"    {\n",[376,1134,1135],{"class":378,"line":414},[376,1136,1137],{},"      \"name\": \"EMAIL\",\n",[376,1139,1140],{"class":378,"line":421},[376,1141,1142],{},"      \"allowed_sender_addresses\": [\n",[376,1144,1145],{"class":378,"line":432},[376,1146,1147],{},"        \"noreply@example.com\"\n",[376,1149,1150],{"class":378,"line":470},[376,1151,1152],{},"      ]\n",[376,1154,1155],{"class":378,"line":491},[376,1156,581],{},[376,1158,1159],{"class":378,"line":522},[376,1160,1161],{},"  ],\n",[376,1163,1164],{"class":378,"line":527},[376,1165,1166],{},"  \"vars\": {\n",[376,1168,1169],{"class":378,"line":555},[376,1170,1171],{},"    \"EMAIL_FROM\": \"noreply@example.com\"\n",[376,1173,1174],{"class":378,"line":572},[376,1175,1176],{},"  }\n",[376,1178,1179],{"class":378,"line":578},[376,1180,411],{},[11,1182,1183,1186],{},[15,1184,1185],{},"allowed_sender_addresses"," 不只是文档，它是运行时权限边界。即使某个接口被误用，这个绑定也不能伪造其他发件地址。",[11,1188,1189],{},"还可以用以下配置进一步约束收件人：",[35,1191,1192,1198,1204],{},[38,1193,1194,1197],{},[15,1195,1196],{},"destination_address","：固定为一个目标地址；",[38,1199,1200,1203],{},[15,1201,1202],{},"allowed_destination_addresses","：只允许一组目标地址；",[38,1205,1206,1208],{},[15,1207,1185],{},"：限制可使用的发件地址。",[11,1210,1211,1212,1214],{},"用于系统告警的 Worker 最适合固定 ",[15,1213,1196],{},"；用于用户验证码的服务则通常限制发件人，并在应用层校验收件人和频率。",[179,1216,1218],{"id":1217},"在-worker-中发送","在 Worker 中发送",[140,1220,1222],{"className":370,"code":1221,"language":372,"meta":146,"style":146},"interface Env {\n  EMAIL: SendEmail\n}\n\nexport default {\n  async fetch(_request, env): Promise\u003CResponse> {\n    const result = await env.EMAIL.send({\n      from: {\n        email: 'noreply@example.com',\n        name: 'Example App',\n      },\n      to: 'reader@example.net',\n      subject: '验证你的邮箱',\n      text: '你的验证码是 482913，5 分钟内有效。',\n      html: '\u003Cp>你的验证码是 \u003Cstrong>482913\u003C\u002Fstrong>，5 分钟内有效。\u003C\u002Fp>',\n    })\n\n    return Response.json({\n      messageId: result.messageId,\n    })\n  },\n} satisfies ExportedHandler\u003CEnv>\n",[15,1223,1224,1232,1242,1246,1250,1258,1287,1312,1317,1327,1337,1342,1352,1362,1372,1382,1386,1390,1403,1408,1412,1416],{"__ignoreMap":146},[376,1225,1226,1228,1230],{"class":378,"line":379},[376,1227,383],{"class":382},[376,1229,387],{"class":386},[376,1231,391],{"class":390},[376,1233,1234,1237,1239],{"class":378,"line":394},[376,1235,1236],{"class":397},"  EMAIL",[376,1238,401],{"class":382},[376,1240,1241],{"class":386}," SendEmail\n",[376,1243,1244],{"class":378,"line":408},[376,1245,411],{"class":390},[376,1247,1248],{"class":378,"line":414},[376,1249,418],{"emptyLinePlaceholder":417},[376,1251,1252,1254,1256],{"class":378,"line":421},[376,1253,424],{"class":382},[376,1255,427],{"class":382},[376,1257,391],{"class":390},[376,1259,1260,1262,1265,1267,1270,1272,1274,1276,1278,1280,1282,1285],{"class":378,"line":432},[376,1261,435],{"class":382},[376,1263,1264],{"class":386}," fetch",[376,1266,441],{"class":390},[376,1268,1269],{"class":397},"_request",[376,1271,447],{"class":390},[376,1273,450],{"class":397},[376,1275,453],{"class":390},[376,1277,401],{"class":382},[376,1279,458],{"class":386},[376,1281,461],{"class":390},[376,1283,1284],{"class":386},"Response",[376,1286,467],{"class":390},[376,1288,1289,1291,1294,1296,1298,1301,1304,1307,1310],{"class":378,"line":470},[376,1290,473],{"class":382},[376,1292,1293],{"class":404}," result",[376,1295,479],{"class":382},[376,1297,950],{"class":382},[376,1299,1300],{"class":390}," env.",[376,1302,1303],{"class":404},"EMAIL",[376,1305,1306],{"class":390},".",[376,1308,1309],{"class":386},"send",[376,1311,973],{"class":390},[376,1313,1314],{"class":378,"line":491},[376,1315,1316],{"class":390},"      from: {\n",[376,1318,1319,1322,1325],{"class":378,"line":522},[376,1320,1321],{"class":390},"        email: ",[376,1323,1324],{"class":509},"'noreply@example.com'",[376,1326,662],{"class":390},[376,1328,1329,1332,1335],{"class":378,"line":527},[376,1330,1331],{"class":390},"        name: ",[376,1333,1334],{"class":509},"'Example App'",[376,1336,662],{"class":390},[376,1338,1339],{"class":378,"line":555},[376,1340,1341],{"class":390},"      },\n",[376,1343,1344,1347,1350],{"class":378,"line":572},[376,1345,1346],{"class":390},"      to: ",[376,1348,1349],{"class":509},"'reader@example.net'",[376,1351,662],{"class":390},[376,1353,1354,1357,1360],{"class":378,"line":578},[376,1355,1356],{"class":390},"      subject: ",[376,1358,1359],{"class":509},"'验证你的邮箱'",[376,1361,662],{"class":390},[376,1363,1364,1367,1370],{"class":378,"line":584},[376,1365,1366],{"class":390},"      text: ",[376,1368,1369],{"class":509},"'你的验证码是 482913，5 分钟内有效。'",[376,1371,662],{"class":390},[376,1373,1374,1377,1380],{"class":378,"line":589},[376,1375,1376],{"class":390},"      html: ",[376,1378,1379],{"class":509},"'\u003Cp>你的验证码是 \u003Cstrong>482913\u003C\u002Fstrong>，5 分钟内有效。\u003C\u002Fp>'",[376,1381,662],{"class":390},[376,1383,1384],{"class":378,"line":605},[376,1385,1007],{"class":390},[376,1387,1388],{"class":378,"line":622},[376,1389,418],{"emptyLinePlaceholder":417},[376,1391,1392,1395,1398,1401],{"class":378,"line":653},[376,1393,1394],{"class":382},"    return",[376,1396,1397],{"class":390}," Response.",[376,1399,1400],{"class":386},"json",[376,1402,973],{"class":390},[376,1404,1405],{"class":378,"line":665},[376,1406,1407],{"class":390},"      messageId: result.messageId,\n",[376,1409,1410],{"class":378,"line":671},[376,1411,1007],{"class":390},[376,1413,1414],{"class":378,"line":677},[376,1415,735],{"class":390},[376,1417,1418,1420,1422,1424,1426,1428],{"class":378,"line":682},[376,1419,741],{"class":390},[376,1421,744],{"class":382},[376,1423,747],{"class":386},[376,1425,461],{"class":390},[376,1427,752],{"class":386},[376,1429,755],{"class":390},[11,1431,1432,1433,1435],{},"Workers binding 成功时返回一个 ",[15,1434,135],{},"。这个结果代表 Cloudflare 接受了发送请求，不应该被解释为用户一定已经在收件箱里看到邮件。最终投递、延迟、退信和抑制状态还要结合 Email Service 日志与指标。",[179,1437,1439],{"id":1438},"在-nuxt-服务端-api-中发送","在 Nuxt 服务端 API 中发送",[11,1441,1442],{},"Nuxt 部署到 Workers 后，同样可以从服务端路由读取 binding：",[140,1444,1446],{"className":370,"code":1445,"language":372,"meta":146,"style":146},"import { z } from 'zod'\n\nconst schema = z.object({\n  email: z.string().trim().email().max(254),\n})\n\nexport default defineEventHandler(async (event) => {\n  const { email } = schema.parse(await readBody(event))\n  const env = event.context.cloudflare.env as {\n    EMAIL: SendEmail\n    EMAIL_FROM: string\n  }\n\n  \u002F\u002F 实际项目还应在这里执行同源校验、IP\u002F邮箱限流，\n  \u002F\u002F 并在 D1 中原子领取一次发送资格。\n  const result = await env.EMAIL.send({\n    from: {\n      email: env.EMAIL_FROM,\n      name: 'Example App',\n    },\n    to: email,\n    subject: '验证邮箱',\n    text: '验证码将在 5 分钟后失效。',\n    html: '\u003Cp>验证码将在 \u003Cstrong>5 分钟\u003C\u002Fstrong>后失效。\u003C\u002Fp>',\n  })\n\n  return {\n    ok: true,\n    messageId: result.messageId,\n  }\n})\n",[15,1447,1448,1460,1464,1482,1513,1518,1522,1549,1581,1598,1607,1616,1620,1624,1630,1635,1655,1660,1670,1679,1684,1689,1699,1709,1719,1724,1728,1735,1745,1751,1756],{"__ignoreMap":146},[376,1449,1450,1452,1455,1457],{"class":378,"line":379},[376,1451,895],{"class":382},[376,1453,1454],{"class":390}," { z } ",[376,1456,901],{"class":382},[376,1458,1459],{"class":509}," 'zod'\n",[376,1461,1462],{"class":378,"line":394},[376,1463,418],{"emptyLinePlaceholder":417},[376,1465,1466,1469,1472,1474,1477,1480],{"class":378,"line":408},[376,1467,1468],{"class":382},"const",[376,1470,1471],{"class":404}," schema",[376,1473,479],{"class":382},[376,1475,1476],{"class":390}," z.",[376,1478,1479],{"class":386},"object",[376,1481,973],{"class":390},[376,1483,1484,1487,1490,1493,1496,1498,1501,1503,1506,1508,1511],{"class":378,"line":414},[376,1485,1486],{"class":390},"  email: z.",[376,1488,1489],{"class":386},"string",[376,1491,1492],{"class":390},"().",[376,1494,1495],{"class":386},"trim",[376,1497,1492],{"class":390},[376,1499,1500],{"class":386},"email",[376,1502,1492],{"class":390},[376,1504,1505],{"class":386},"max",[376,1507,441],{"class":390},[376,1509,1510],{"class":404},"254",[376,1512,650],{"class":390},[376,1514,1515],{"class":378,"line":421},[376,1516,1517],{"class":390},"})\n",[376,1519,1520],{"class":378,"line":432},[376,1521,418],{"emptyLinePlaceholder":417},[376,1523,1524,1526,1528,1531,1533,1536,1539,1542,1544,1547],{"class":378,"line":470},[376,1525,424],{"class":382},[376,1527,427],{"class":382},[376,1529,1530],{"class":386}," defineEventHandler",[376,1532,441],{"class":390},[376,1534,1535],{"class":382},"async",[376,1537,1538],{"class":390}," (",[376,1540,1541],{"class":397},"event",[376,1543,513],{"class":390},[376,1545,1546],{"class":382},"=>",[376,1548,391],{"class":390},[376,1550,1551,1554,1557,1559,1562,1565,1568,1570,1572,1575,1578],{"class":378,"line":491},[376,1552,1553],{"class":382},"  const",[376,1555,1556],{"class":390}," { ",[376,1558,1500],{"class":404},[376,1560,1561],{"class":390}," } ",[376,1563,1564],{"class":382},"=",[376,1566,1567],{"class":390}," schema.",[376,1569,956],{"class":386},[376,1571,441],{"class":390},[376,1573,1574],{"class":382},"await",[376,1576,1577],{"class":386}," readBody",[376,1579,1580],{"class":390},"(event))\n",[376,1582,1583,1585,1588,1590,1593,1596],{"class":378,"line":522},[376,1584,1553],{"class":382},[376,1586,1587],{"class":404}," env",[376,1589,479],{"class":382},[376,1591,1592],{"class":390}," event.context.cloudflare.env ",[376,1594,1595],{"class":382},"as",[376,1597,391],{"class":390},[376,1599,1600,1603,1605],{"class":378,"line":527},[376,1601,1602],{"class":397},"    EMAIL",[376,1604,401],{"class":382},[376,1606,1241],{"class":386},[376,1608,1609,1612,1614],{"class":378,"line":555},[376,1610,1611],{"class":397},"    EMAIL_FROM",[376,1613,401],{"class":382},[376,1615,405],{"class":404},[376,1617,1618],{"class":378,"line":572},[376,1619,1176],{"class":390},[376,1621,1622],{"class":378,"line":578},[376,1623,418],{"emptyLinePlaceholder":417},[376,1625,1626],{"class":378,"line":584},[376,1627,1629],{"class":1628},"sJ8bj","  \u002F\u002F 实际项目还应在这里执行同源校验、IP\u002F邮箱限流，\n",[376,1631,1632],{"class":378,"line":589},[376,1633,1634],{"class":1628},"  \u002F\u002F 并在 D1 中原子领取一次发送资格。\n",[376,1636,1637,1639,1641,1643,1645,1647,1649,1651,1653],{"class":378,"line":605},[376,1638,1553],{"class":382},[376,1640,1293],{"class":404},[376,1642,479],{"class":382},[376,1644,950],{"class":382},[376,1646,1300],{"class":390},[376,1648,1303],{"class":404},[376,1650,1306],{"class":390},[376,1652,1309],{"class":386},[376,1654,973],{"class":390},[376,1656,1657],{"class":378,"line":622},[376,1658,1659],{"class":390},"    from: {\n",[376,1661,1662,1665,1668],{"class":378,"line":653},[376,1663,1664],{"class":390},"      email: env.",[376,1666,1667],{"class":404},"EMAIL_FROM",[376,1669,662],{"class":390},[376,1671,1672,1675,1677],{"class":378,"line":665},[376,1673,1674],{"class":390},"      name: ",[376,1676,1334],{"class":509},[376,1678,662],{"class":390},[376,1680,1681],{"class":378,"line":671},[376,1682,1683],{"class":390},"    },\n",[376,1685,1686],{"class":378,"line":677},[376,1687,1688],{"class":390},"    to: email,\n",[376,1690,1691,1694,1697],{"class":378,"line":682},[376,1692,1693],{"class":390},"    subject: ",[376,1695,1696],{"class":509},"'验证邮箱'",[376,1698,662],{"class":390},[376,1700,1701,1704,1707],{"class":378,"line":702},[376,1702,1703],{"class":390},"    text: ",[376,1705,1706],{"class":509},"'验证码将在 5 分钟后失效。'",[376,1708,662],{"class":390},[376,1710,1711,1714,1717],{"class":378,"line":707},[376,1712,1713],{"class":390},"    html: ",[376,1715,1716],{"class":509},"'\u003Cp>验证码将在 \u003Cstrong>5 分钟\u003C\u002Fstrong>后失效。\u003C\u002Fp>'",[376,1718,662],{"class":390},[376,1720,1721],{"class":378,"line":712},[376,1722,1723],{"class":390},"  })\n",[376,1725,1726],{"class":378,"line":717},[376,1727,418],{"emptyLinePlaceholder":417},[376,1729,1730,1733],{"class":378,"line":732},[376,1731,1732],{"class":382},"  return",[376,1734,391],{"class":390},[376,1736,1737,1740,1743],{"class":378,"line":738},[376,1738,1739],{"class":390},"    ok: ",[376,1741,1742],{"class":404},"true",[376,1744,662],{"class":390},[376,1746,1748],{"class":378,"line":1747},29,[376,1749,1750],{"class":390},"    messageId: result.messageId,\n",[376,1752,1754],{"class":378,"line":1753},30,[376,1755,1176],{"class":390},[376,1757,1759],{"class":378,"line":1758},31,[376,1760,1517],{"class":390},[11,1762,1763],{},"不要把这个示例直接变成一个匿名、无限频率、可自由填写主题和正文的公开 API。那会把自己的域名变成垃圾邮件跳板。",[11,1765,1766],{},[161,1767],{"alt":1768,"src":1769},"事务邮件从 Worker 生成，经过多层身份认证后投递到不同收件系统","\u002Fmedia\u002Fposts\u002Fcloudflare-email-service-send-receive-guide\u002Foutbound-delivery-v1.webp",[11,1771,1772],{},[168,1773,1774],{},"SPF、DKIM 与 DMARC 解决的是“谁有权发送、内容是否被修改、失败时如何处置”，并不保证每封邮件都进入主收件箱。",[73,1776,1778],{"id":1777},"rest-api-与认证-smtp-什么时候更合适","REST API 与认证 SMTP 什么时候更合适",[11,1780,1781],{},"Cloudflare 目前提供三种发送入口：",[81,1783,1784,1797],{},[84,1785,1786],{},[87,1787,1788,1791,1794],{},[90,1789,1790],{},"方式",[90,1792,1793],{},"最适合",[90,1795,1796],{},"凭证与边界",[103,1798,1799,1810,1821],{},[87,1800,1801,1804,1807],{},[108,1802,1803],{},"Workers binding",[108,1805,1806],{},"已运行在 Workers 的 Nuxt、Hono、Agent",[108,1808,1809],{},"不在代码中保存 API Token，可通过 binding 限制发件人与收件人",[87,1811,1812,1815,1818],{},[108,1813,1814],{},"REST API",[108,1816,1817],{},"运行在其他云、CI 或后端平台的程序",[108,1819,1820],{},"使用最小权限 API Token，响应会按收件人区分 delivered、queued、permanent bounce",[87,1822,1823,1826,1829],{},[108,1824,1825],{},"认证 SMTP",[108,1827,1828],{},"已经使用 Nodemailer、PHPMailer、JavaMail 等 SMTP 客户端的旧系统",[108,1830,1831,1834],{},[15,1832,1833],{},"smtp.mx.cloudflare.net:465","，仅隐式 TLS，密码是有 Email Sending 权限的 API Token",[11,1836,1837],{},"认证 SMTP 是 2026 年 6 月加入的 Beta 能力。当前只支持 465 端口的 SMTPS，不提供 587 端口 STARTTLS，也不是可以匿名使用的 25 端口 relay。",[11,1839,1840],{},"SMTP Token 一旦泄漏，持有者可以使用账户中已接入的发件域发送邮件。因此：",[35,1842,1843,1846,1852,1855,1858],{},[38,1844,1845],{},"优先使用 account-owned token；",[38,1847,1848,1849,341],{},"权限只给 ",[15,1850,1851],{},"Email Sending: Edit",[38,1853,1854],{},"不写进仓库、镜像或前端环境变量；",[38,1856,1857],{},"不同系统使用不同 Token，便于单独吊销；",[38,1859,1860],{},"能用 Worker binding 的场景，不要为了“熟悉 SMTP”额外引入长期凭证。",[73,1862,1863],{"id":1863},"必须知道的限制",[11,1865,1866],{},"截至 2026 年 7 月，官方平台限制中最值得在代码里提前防守的是：",[81,1868,1869,1883],{},[84,1870,1871],{},[87,1872,1873,1876,1880],{},[90,1874,1875],{},"限制",[90,1877,1879],{"align":1878},"right","当前值",[90,1881,1882],{},"实现影响",[103,1884,1885,1896,1906,1917,1928,1939,1949,1960,1971],{},[87,1886,1887,1890,1893],{},[108,1888,1889],{},"每域 Email Routing 规则",[108,1891,1892],{"align":1878},"200",[108,1894,1895],{},"大量租户不要为每个人创建独立静态规则",[87,1897,1898,1901,1903],{},[108,1899,1900],{},"每账户已验证目标地址",[108,1902,1892],{"align":1878},[108,1904,1905],{},"转发到多邮箱前先规划账户级目标",[87,1907,1908,1911,1914],{},[108,1909,1910],{},"入站邮件大小",[108,1912,1913],{"align":1878},"25 MiB",[108,1915,1916],{},"更大邮件在 SMTP 阶段被拒绝",[87,1918,1919,1922,1925],{},[108,1920,1921],{},"单封出站收件人数",[108,1923,1924],{"align":1878},"50",[108,1926,1927],{},"To、Cc、Bcc 合计",[87,1929,1930,1933,1936],{},[108,1931,1932],{},"普通出站邮件总大小",[108,1934,1935],{"align":1878},"5 MiB",[108,1937,1938],{},"包含附件与 MIME 编码开销",[87,1940,1941,1944,1946],{},[108,1942,1943],{},"发往已验证目标的总大小",[108,1945,1913],{"align":1878},[108,1947,1948],{},"仅适用于已验证目标地址",[87,1950,1951,1954,1957],{},[108,1952,1953],{},"自定义头总大小",[108,1955,1956],{"align":1878},"16 KB",[108,1958,1959],{},"不要把业务数据塞进 Header",[87,1961,1962,1965,1968],{},[108,1963,1964],{},"单个 Zone 的发送\u002F路由域名总数",[108,1966,1967],{"align":1878},"30",[108,1969,1970],{},"根域与子域都计入",[87,1972,1973,1979,1982],{},[108,1974,1975,1976,1978],{},"回复的 ",[15,1977,1092],{}," 项数",[108,1980,1981],{"align":1878},"100",[108,1983,1984,1985,1988],{},"超过后 ",[15,1986,1987],{},"reply()"," 会失败",[11,1990,1991],{},"附件会经过 Base64 和 MIME 封装。3 MiB 的原始附件不等于最终邮件仍是 3 MiB，所以应用限制应给编码、正文和邮件头预留空间。",[11,1993,1994],{},"平台还会根据账户信誉、投递表现和使用历史调整每日发送额度。不要假设“每月额度未用完”就等于某一秒可以无限并发发送。",[73,1996,1997],{"id":1997},"验证码与密码重置应该怎样做",[11,1999,2000],{},"本博客的注册和密码重置邮件采用的是下面这条链路：",[140,2002,2005],{"className":2003,"code":2004,"language":145,"meta":146},[143],"用户提交邮箱\n  → 同源检查\n  → IP 与邮箱限流\n  → 生成 6 位验证码\n  → D1 只保存加盐哈希、用途、过期时间和尝试次数\n  → EMAIL binding 发送\n  → 验证成功后原子标记已使用\n",[15,2006,2004],{"__ignoreMap":146},[11,2008,2009],{},"关键策略包括：",[35,2011,2012,2015,2018,2021,2024,2027,2030],{},[38,2013,2014],{},"同一邮箱 5 分钟只能发送一封；",[38,2016,2017],{},"验证码 5 分钟后失效；",[38,2019,2020],{},"验证码只能使用一次；",[38,2022,2023],{},"D1 不保存验证码明文；",[38,2025,2026],{},"连续输错达到阈值后停止验证；",[38,2028,2029],{},"邮件发送失败时释放本次占位，避免用户被一个未送达的验证码锁住；",[38,2031,2032],{},"重置密码后撤销已有登录会话，而不是只替换密码哈希。",[11,2034,2035,2036,2039],{},"限流要在数据库中“原子领取资格”，不能写成“先查询上次发送时间，再插入一行”。两个并发请求可能同时通过查询。使用唯一键配合 ",[15,2037,2038],{},"INSERT ... ON CONFLICT ... WHERE","，才能让数据库决定谁获得发送资格。",[11,2041,2042],{},"邮件接口的响应也不应暴露“这个邮箱是否已经注册”。否则攻击者可以批量枚举用户。",[73,2044,2045],{"id":2045},"一个更完整的生产架构",[11,2047,2048],{},"当邮件从 Demo 进入生产，推荐把职责拆开：",[140,2050,2053],{"className":2051,"code":2052,"language":145,"meta":146},[143],"HTTP 请求 \u002F 业务事件\n  → 鉴权、同源、参数校验、限流、幂等\n  → Queue\n  → 邮件模板渲染\n  → EMAIL binding\n  → messageId \u002F 错误码写入 D1\n  → Email Service 日志与告警\n",[15,2054,2052],{"__ignoreMap":146},[11,2056,2057],{},"入站链路则可以是：",[140,2059,2062],{"className":2060,"code":2061,"language":145,"meta":146},[143],"support@yourdomain.com\n  → Email Routing\n  → Email Worker\n  → 认证与大小检查\n  → MIME 解析\n  → R2 保存附件 \u002F D1 建立工单\n  → Queue 做耗时处理\n  → 转发人工邮箱或发送受约束回复\n",[15,2063,2061],{"__ignoreMap":146},[179,2065,2067],{"id":2066},"d1-建议记录什么","D1 建议记录什么",[11,2069,2070],{},"可以记录：",[35,2072,2073,2076,2079,2083,2086,2089,2092],{},[38,2074,2075],{},"业务事件 ID 与幂等键；",[38,2077,2078],{},"模板 ID 和模板版本；",[38,2080,2081,341],{},[15,2082,135],{},[38,2084,2085],{},"收件人数，不一定要保存完整地址；",[38,2087,2088],{},"accepted \u002F failed 等应用侧状态；",[38,2090,2091],{},"Cloudflare 错误码；",[38,2093,2094],{},"创建、发送和最后更新时间。",[11,2096,2097],{},"不建议默认记录：",[35,2099,2100,2103,2106,2109,2112,2115],{},[38,2101,2102],{},"验证码明文；",[38,2104,2105],{},"密码重置 Token；",[38,2107,2108],{},"完整邮件正文；",[38,2110,2111],{},"附件内容；",[38,2113,2114],{},"SMTP\u002FAPI Token；",[38,2116,2117],{},"为调试方便长期保留的原始来信。",[11,2119,2120],{},"日志本身也可能成为新的隐私数据源。",[179,2122,2124],{"id":2123},"不要把-email-routing-的-dropped-当成发送失败","不要把 Email Routing 的 “Dropped” 当成发送失败",[11,2126,2127,2128,2131,2132,2135],{},"官方特别说明：通过 ",[15,2129,2130],{},"send_email"," binding 发出的邮件，在 Email Routing 汇总页面可能显示为 ",[26,2133,2134],{},"dropped","，即使邮件实际已经成功投递。",[11,2137,2138,2139,2141],{},"出站结果应该查看 Email Sending 的 Metrics 与 Logs，并用 ",[15,2140,135],{}," 关联应用日志。Routing 页面描述的是入站路由处理，不是完整的出站投递状态机。",[73,2143,2144],{"id":2144},"本地怎么测试",[11,2146,2147],{},"Wrangler 已支持本地触发 Email Worker。运行：",[140,2149,2153],{"className":2150,"code":2151,"language":2152,"meta":146,"style":146},"language-bash shiki shiki-themes github-dark github-dark","pnpm exec wrangler dev\n","bash",[15,2154,2155],{"__ignoreMap":146},[376,2156,2157,2160,2163,2166],{"class":378,"line":379},[376,2158,2159],{"class":386},"pnpm",[376,2161,2162],{"class":509}," exec",[376,2164,2165],{"class":509}," wrangler",[376,2167,2168],{"class":509}," dev\n",[11,2170,2171],{},"然后向本地入口发送一封原始邮件：",[140,2173,2175],{"className":2150,"code":2174,"language":2152,"meta":146,"style":146},"curl -X POST \\\n  'http:\u002F\u002Flocalhost:8787\u002Fcdn-cgi\u002Fhandler\u002Femail?from=sender@example.net&to=support@example.com' \\\n  -H 'Content-Type: text\u002Fplain' \\\n  --data-binary $'From: sender@example.net\\nTo: support@example.com\\nSubject: Local test\\n\\nHello from local development.'\n",[15,2176,2177,2191,2198,2208],{"__ignoreMap":146},[376,2178,2179,2182,2185,2188],{"class":378,"line":379},[376,2180,2181],{"class":386},"curl",[376,2183,2184],{"class":404}," -X",[376,2186,2187],{"class":509}," POST",[376,2189,2190],{"class":404}," \\\n",[376,2192,2193,2196],{"class":378,"line":394},[376,2194,2195],{"class":509},"  'http:\u002F\u002Flocalhost:8787\u002Fcdn-cgi\u002Fhandler\u002Femail?from=sender@example.net&to=support@example.com'",[376,2197,2190],{"class":404},[376,2199,2200,2203,2206],{"class":378,"line":408},[376,2201,2202],{"class":404},"  -H",[376,2204,2205],{"class":509}," 'Content-Type: text\u002Fplain'",[376,2207,2190],{"class":404},[376,2209,2210,2213,2216,2219,2222,2224,2227,2230],{"class":378,"line":414},[376,2211,2212],{"class":404},"  --data-binary",[376,2214,2215],{"class":509}," $'From: sender@example.net",[376,2217,2218],{"class":404},"\\n",[376,2220,2221],{"class":509},"To: support@example.com",[376,2223,2218],{"class":404},[376,2225,2226],{"class":509},"Subject: Local test",[376,2228,2229],{"class":404},"\\n\\n",[376,2231,2232],{"class":509},"Hello from local development.'\n",[11,2234,2235],{},"本地发送 binding 会把文本和 HTML 保存成可检查的文件，适合验证模板。二进制附件的本地模拟仍有单独限制，涉及附件时应补一轮远程测试。",[11,2237,2238],{},"至少覆盖这些用例：",[35,2240,2241,2244,2247,2250,2253,2256,2259,2262,2265,2268],{},[38,2242,2243],{},"正常文本和 HTML；",[38,2245,2246],{},"非 ASCII 主题与发件人显示名；",[38,2248,2249],{},"重复请求与幂等；",[38,2251,2252],{},"超过发送冷却时间前的重复验证码；",[38,2254,2255],{},"大正文与附件边界；",[38,2257,2258],{},"Header 换行注入；",[38,2260,2261],{},"未验证发件域或受限收件人；",[38,2263,2264],{},"退信、抑制、额度和频率错误；",[38,2266,2267],{},"入站无匹配地址、Catch-all 和拒绝路径；",[38,2269,2270],{},"HTML 邮件中的脚本、远程资源与危险附件名。",[73,2272,2274],{"id":2273},"它适合什么不适合什么","它适合什么，不适合什么",[11,2276,2277],{},"适合：",[35,2279,2280,2283,2286,2289,2292,2295],{},[38,2281,2282],{},"注册验证、魔法链接、密码重置；",[38,2284,2285],{},"订单、账单、部署、监控通知；",[38,2287,2288],{},"个人域名邮件转发；",[38,2290,2291],{},"联系表单与支持工单；",[38,2293,2294],{},"邮件触发的 Agent 或自动化流程；",[38,2296,2297],{},"希望少维护一台 SMTP 服务的 Cloudflare 应用。",[11,2299,2300],{},"不应直接拿来做：",[35,2302,2303,2306,2309,2312,2315],{},[38,2304,2305],{},"未经同意的营销群发；",[38,2307,2308],{},"没有退订、投诉和抑制管理的 Newsletter；",[38,2310,2311],{},"匿名开放的“帮我发邮件”接口；",[38,2313,2314],{},"需要 IMAP 文件夹、日历、通讯录和多人邮箱协作的企业邮箱；",[38,2316,2317],{},"把任何来信正文直接交给有高权限工具的 Agent。",[11,2319,2320],{},"Cloudflare 替你维护了邮件入口与投递基础设施，但发件人信誉、用户授权、内容安全、频率控制和数据最小化仍然是应用自己的责任。",[73,2322,2323],{"id":2323},"最后的判断",[11,2325,2326],{},"Cloudflare Email Service 最有价值的地方，不是“又多了一个 SMTP 厂商”，而是它让邮件和 Worker、D1、R2、Queue、Agents 处于同一运行边界。",[11,2328,2329],{},"接收邮件不再只能转发到人类邮箱，它可以成为一个受约束的事件入口；发送邮件也不再需要为验证码单独接入一个外部 SDK 和密钥体系。",[11,2331,2332],{},"如果项目本来就部署在 Workers，优先从这条最小链路开始：",[331,2334,2335,2341,2344,2347,2350],{},[38,2336,2337,2338,341],{},"Email Routing 接收 ",[15,2339,2340],{},"support@",[38,2342,2343],{},"Worker binding 发送验证码和密码重置；",[38,2345,2346],{},"D1 实现冷却、一次性验证和最小日志；",[38,2348,2349],{},"sender allowlist 限制 binding 权限；",[38,2351,2352],{},"用 Email Sending 日志核对投递，而不是只看接口返回成功。",[11,2354,2355],{},"等确实需要从外部平台发送时，再引入 REST API；只有现有系统已经深度依赖 SMTP 客户端时，才使用认证 SMTP。",[73,2357,2358],{"id":2358},"参考资料",[35,2360,2361,2370,2377,2384,2391,2398,2405,2412,2418,2424,2431,2438,2445],{},[38,2362,2363],{},[2364,2365,2369],"a",{"href":2366,"rel":2367},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002F",[2368],"nofollow","Cloudflare Email Service 概览",[38,2371,2372],{},[2364,2373,2376],{"href":2374,"rel":2375},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fget-started\u002Fsend-emails\u002F",[2368],"接入 Email Sending",[38,2378,2379],{},[2364,2380,2383],{"href":2381,"rel":2382},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fget-started\u002Froute-emails\u002F",[2368],"接入 Email Routing",[38,2385,2386],{},[2364,2387,2390],{"href":2388,"rel":2389},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fconfiguration\u002Fdomains\u002F",[2368],"域名与 DNS 配置",[38,2392,2393],{},[2364,2394,2397],{"href":2395,"rel":2396},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fconfiguration\u002Fsend-bindings\u002F",[2368],"发送 binding 权限配置",[38,2399,2400],{},[2364,2401,2404],{"href":2402,"rel":2403},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fapi\u002Fsend-emails\u002Fworkers-api\u002F",[2368],"Workers 发送 API",[38,2406,2407],{},[2364,2408,2411],{"href":2409,"rel":2410},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fapi\u002Froute-emails\u002Femail-handler\u002F",[2368],"Email Worker 接收 API",[38,2413,2414],{},[2364,2415,1814],{"href":2416,"rel":2417},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fapi\u002Fsend-emails\u002Frest-api\u002F",[2368],[38,2419,2420],{},[2364,2421,1825],{"href":2422,"rel":2423},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fapi\u002Fsend-emails\u002Fsmtp\u002F",[2368],[38,2425,2426],{},[2364,2427,2430],{"href":2428,"rel":2429},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fplatform\u002Flimits\u002F",[2368],"平台限制",[38,2432,2433],{},[2364,2434,2437],{"href":2435,"rel":2436},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fplatform\u002Fpricing\u002F",[2368],"价格说明",[38,2439,2440],{},[2364,2441,2444],{"href":2442,"rel":2443},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Fconcepts\u002Femail-authentication\u002F",[2368],"邮件认证与投递",[38,2446,2447],{},[2364,2448,2451],{"href":2449,"rel":2450},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002Flocal-development\u002Frouting\u002F",[2368],"本地测试 Email Worker",[2453,2454,2455],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sOPea, html code.shiki .sOPea{--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFR8T, html code.shiki .sFR8T{--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s-3mD, html code.shiki .s-3mD{--shiki-default:#FFAB70;--shiki-dark:#FFAB70}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":146,"searchDepth":408,"depth":408,"links":2457},[2458,2459,2463,2468,2474,2478,2479,2480,2481,2485,2486,2487,2488],{"id":75,"depth":394,"text":76},{"id":173,"depth":394,"text":174,"children":2460},[2461,2462],{"id":181,"depth":408,"text":182},{"id":213,"depth":408,"text":214},{"id":240,"depth":394,"text":241,"children":2464},[2465,2466],{"id":247,"depth":408,"text":247},{"id":267,"depth":408,"text":2467},"发送邮件使用 cf-bounce 记录",{"id":325,"depth":394,"text":326,"children":2469},[2470,2472,2473],{"id":353,"depth":408,"text":2471},"email() 是入站邮件入口",{"id":878,"depth":408,"text":879},{"id":1043,"depth":408,"text":1044},{"id":1099,"depth":394,"text":1100,"children":2475},[2476,2477],{"id":1217,"depth":408,"text":1218},{"id":1438,"depth":408,"text":1439},{"id":1777,"depth":394,"text":1778},{"id":1863,"depth":394,"text":1863},{"id":1997,"depth":394,"text":1997},{"id":2045,"depth":394,"text":2045,"children":2482},[2483,2484],{"id":2066,"depth":408,"text":2067},{"id":2123,"depth":408,"text":2124},{"id":2144,"depth":394,"text":2144},{"id":2273,"depth":394,"text":2274},{"id":2323,"depth":394,"text":2323},{"id":2358,"depth":394,"text":2358},"截至 2026 年 7 月，系统介绍 Cloudflare Email Service 如何接收、转发、解析、回复和发送事务邮件，并给出 Nuxt、Workers、D1 与安全限流实践。",false,null,"md",{"src":2494,"alt":2495,"caption":2496,"width":2497,"height":2498},"\u002Fmedia\u002Fposts\u002Fcloudflare-email-service-send-receive-guide\u002Fcover-v1.webp","邮件经过全球边缘网络中的 Worker 节点完成接收、认证与发送","Cloudflare Email Service 正在把邮件从一台需要维护的服务器，变成一种可以由代码处理的事件",1600,900,{},"\u002Fblog\u002Fcloudflare-email-service-send-receive-guide","2026-07-28",{"title":5,"description":2489},"blog\u002Fcloudflare-email-service-send-receive-guide",[2505,2506,2507,2508],"Cloudflare","Email","Workers","Serverless","PEMKm6krZEYvimccpAfGivWWE08wKBwH9F_Kheci_jo",[2491,2511],{"title":2512,"path":2513,"stem":2514,"description":2515,"children":-1},"在边缘构建有状态应用","\u002Fblog\u002Fedge-stateful-apps","blog\u002Fedge-stateful-apps","Cloudflare Workers 很快，D1 很近，但真正困难的是把状态、一致性与失败边界设计清楚。",1785205550130]