Cloudflare 全家桶扫盲 查证于 2026-07-31
☁️ 逐条核实 · 现查官方文档

Cloudflare 全家桶
扫盲

写给会 Next.js + Vercel + Supabase、但一次没碰过 Cloudflare 的人。 起因是小红书博主 John Wayne(signerlabs)那期《Cloudflare 全家桶,小团队开发第一选择》—— 他讲的 11 条大方向没问题,但有三处会让新手实际卡住的硬钉子,他一条都没提。

查证日期 2026-07-31 覆盖 Workers · R2 · D1 · KV · Bindings · Hono 额度数字 均引官方文档 ⚠️ 超过三个月请重新核对
硬钉子 01

自定义域名必须迁 NS

Custom Domain 要求域名是 Cloudflare 的 active zone,只加 CNAME 不行,Partial Setup 仅限 Business 以上套餐。

硬钉子 02

开通 R2 必须先绑卡

即使全程停在免费额度内,开通也要走完 checkout 录入支付信息。Workers 和 Pages 都不用,只有 R2 要。

硬钉子 03

免费版发不出邮件

Email Service 出站能力只在 Workers Paid($5/月起)开放,免费版入站无限、出站不可用。

〇、先看结论:博主那 11 条,逐条核对#

# 博主的说法 核实结果
1 Workers 是 serverless 函数,跑全球边缘节点,冷启动几乎为 0 ,而且原理比"serverless 函数"更特别,见第一章
2 Hono + Workers,连 GitHub 后 push 自动构建部署 ,功能叫 Workers Builds
3 D1 是 serverless SQLite,migrations 文件夹一放一条命令建表 机制对
4 R2 是 S3 平替,杀手锏出口流量完全免费 ,但漏了一条:开通必须先绑信用卡
5 相册、房型图全放 R2,用户随便刷不心疼 对,这是 R2 最实在的优势
6 Worker 里直接把通知邮件发出去 ⚠️ 要分情况:发给自己(已验证地址)免费版就能做,发给陌生收件人才要 Paid,见第九章
7 域名绑定一次全子域共用,第三方邮件服务都不用接 ⚠️ 前半句有硬前提;后半句只在「发给自己」时成立
8 一主域按业态排子域,每子域绑独立 Worker,SSL 全自动 对,前提是域名 NS 在 Cloudflare
9 配置文件加一行 observability enabled
10 控制台直接查每个请求日志 对,免费版只留 3 天
11 Next.js 整站用 OpenNext 跑在 Workers 上,连 Vercel 都省了;免费额度大方:Workers 每天 10 万请求、D1 给 5GB、每天 500 万行读、R2 给 10GB、出口永不收钱 ⚠️ 数字有一处错:D1 的 5GB 是整个账户总量,单个数据库免费版只有 500MB。另外 OpenNext 整站在免费版上很容易撞 3MB 体积墙

一句话总结:博主讲的大方向没问题,全家桶确实好用。但有三处会让新手实际卡住——域名必须迁 NSR2 必须绑卡免费版发不了给陌生人的邮件(发给自己是免费的,见第九章订正)。这三条他都没提。


一、Workers 到底是什么#

用你熟的东西对比#

你现在的心智模型大概是这样:

示意
Vercel Serverless Function / AWS Lambda
  → 请求来了 → 起一个容器 → 容器里装着完整的 Node.js → 跑你的代码 → 干掉容器
  → 冷启动 = 起容器 + 起 Node 进程的时间 ≈ 100ms ~ 几秒

Workers 不是这个模型。它是:

示意
Cloudflare Workers
  → 每台边缘机器上,V8 引擎(Chrome 那个 JS 引擎)一直开着
  → 请求来了 → 在这个已经跑着的 V8 里,新开一个 isolate(隔离沙箱)→ 跑你的代码
  → 冷启动 = 在内存里 new 一个沙箱的时间 ≈ 5ms 以内

关键区别:它不给你起进程,只给你起沙箱。 官方原文说一个 isolate 的启动速度大约是 Node 进程的一百倍,启动内存开销低一个数量级。 (来源:https://developers.cloudflare.com/workers/reference/how-workers-works/)

"跑在全球 300+ 节点"具体意味着什么#

不是"节点多所以快"这种玄学。是这样:

  • Vercel/Lambda:你的函数部署在某个 region(比如 iad1 美东)。上海用户请求 → 数据包飞到美东 → 执行 → 飞回来。
  • Workers:同一份代码预先复制到全球每一个 Cloudflare 数据中心。上海用户请求 → 打到上海/香港节点 → 就在那台机器上执行 → 直接返回。

省的是物理距离带来的网络延迟。你现在 Vercel 上那些函数在 iad1,国内访问天然有 200ms+ 的往返,这块是 Workers 的主场。

代价:三条必须知道的硬限制#

天下没有白拿的东西。isolate 模型换来极速冷启动,代价是:

1)它不是 Node.js。

Workers 的运行时叫 workerd,基于 V8,原生不提供任何 Node API。要用 npm 包,得在配置里显式开 nodejs_compat 兼容标志,而且官方明说这个标志永远需要手动开,不会转正为默认

即便开了,这几类包依然跑不起来:

  • 依赖 fs 真实读写磁盘的(Workers 没有持久文件系统)
  • 依赖原生二进制编译产物的(sharpbcrypt 的原生绑定等,.node 扩展加载不了)
  • 体积大的(完整 Puppeteer 之类,直接撞体积墙)

(来源:https://developers.cloudflare.com/workers/configuration/compatibility-flags/)

2)免费版单请求 CPU 时间只有 10 毫秒。

这条是最容易翻车的。注意是 CPU 时间,不是墙钟时间——你 await fetch() 等外部 API 的那几百毫秒不算,只算真正在跑 JS 的时间。

普通的路由转发、查个库、拼个 JSON,通常只用 2~3ms,够用。但只要你在 Worker 里做加解密、大 JSON 解析、图片处理、复杂鉴权,10ms 立刻见底。

这条直接决定了架构:重活必须放到客户端或者别的地方做,Worker 只当薄薄的一层 API。

3)单个 Worker 脚本压缩后不能超过 3MB(免费版)。

注意是 gzip 压缩之后。付费版 10MB。这条在跑 Next.js 整站的时候会正面撞上,见第八章。

完整限制对照表#

项目 Free Paid($5/月起)
每日请求数 10 万/天 无每日硬顶,月包 1000 万,超出 $0.30/百万
单请求 CPU 时间 10 ms 默认 30 秒,可配到 5 分钟
单请求墙钟时长 无限制 无限制
内存 128 MB 128 MB(一样)
脚本体积(gzip 后) 3 MB 10 MB
外部子请求数 50 个/请求 默认 1 万,可提到 1000 万
环境变量数 64 个 128 个
Cron 触发器 5 个 250 个
账户 Worker 数 100 个 500 个

(来源:https://developers.cloudflare.com/workers/platform/limits/ · https://developers.cloudflare.com/workers/platform/pricing/)


二、名字对照表:这堆东西分别是干嘛的#

你问的"对象存储"叫 R2。但 Cloudflare 一口气有九个存储/数据产品,先把名字理顺:

名字 一句话 对标 什么时候用它
R2 对象存储,存文件 AWS S3 / 阿里云 OSS / Vercel Blob 图片、视频、用户上传、备份
KV 全球分布的键值缓存,最终一致 简化版 Redis(偏只读) 配置、Feature Flag、路由表
D1 SQLite 关系型数据库 轻量版 Postgres / Supabase 用户表、订单表,要 SQL 和 JOIN 的
Durable Objects 有状态的单例,计算+存储合一 没有对标,接近 Actor 模型 聊天室、WebSocket 房间、计数器、锁
Queues 消息队列 AWS SQS 异步任务、削峰
Vectorize 向量数据库 Pinecone RAG、语义搜索
Hyperdrive 不是数据库,是连接池+加速层 PgBouncer + 全球加速 你有 Supabase 不想搬,只想在边缘访问它更快
Workers AI 跑 AI 模型的推理平台 Replicate / Bedrock 文本生成、embedding
Cache API 纯边缘 HTTP 缓存,随时被清 CDN 缓存 缓存能重算的响应,不能当数据库

新手最容易混的三个:R2 vs KV vs D1#

用一个具体场景说清楚。假设你做一个物品收纳 App,用户拍照存物品:

  • 物品照片本身(几百 KB 的 PNG)→ 放 R2。它是"文件",你按一个 key 整个存进去、整个取出来,没法对图片内容做查询。
  • 物品的名称、分类、位置、标签、创建时间 → 放 D1。这是"表和行",你要 WHERE category = '厨房'、要 JOIN、要排序,这是关系型数据库的活。
  • "当前 App 版本号"、"首页 banner 配置"这种全站共享、几乎不改的小配置 → 放 KV

为什么不能拿 KV 当数据库? 因为 KV 是最终一致的:你在美国节点写入一个值,其他地区的边缘最多可能要约 60 秒才能读到新值。而且"这个 key 不存在"这个否定结果也会被缓存,所以你刚新建的 key 别人可能读不到。拿它做计数器会直接算错。要强一致就用 Durable Objects

为什么不能把图片塞进 D1? 单行/单字符串上限 2MB,而且免费版单库只有 500MB,几百张图就撑爆了。R2 免费给 10GB 且出口不要钱,这才是存图的地方。


三、R2(对象存储)—— 你问的那个#

它是什么#

S3 的平替,S3 API 兼容,意味着你现有的 AWS SDK 代码基本改个 endpoint 就能用。

支持:桶的增删改查、CORS、生命周期规则、对象的存取删、分片上传全套、CopyObject。 不支持:ACL 访问控制列表、版本控制(Versioning)、桶策略、对象标签、通知/日志。 (来源:https://developers.cloudflare.com/r2/api/s3/api/)

也就是说基本的存取删和分片上传齐全,S3 那些高级治理功能没有。个人项目完全够。

免费额度#

项目 免费
存储量 10 GB-月
Class A 操作(写/列) 100 万次/月
Class B 操作(读) 1000 万次/月
出口流量 完全免费,无上限,不是限时优惠

Class A(贵的,$4.50/百万)PutObjectCopyObjectListObjectsCreateMultipartUploadUploadPartCompleteMultipartUpload —— 上传算 AClass B(便宜的,$0.36/百万)GetObjectHeadObject —— 下载算 B完全免费DeleteObjectDeleteBucketAbortMultipartUpload

(来源:https://developers.cloudflare.com/r2/pricing/)

出口免费到底多值钱:AWS S3 出口流量约 $0.09/GB。你要是有个图片站每月跑 500GB 流量,S3 出口就是 $45/月,R2 是 $0。这是 R2 唯一但足够致命的杀手锏,博主说得没错。

⚠️ 坑一:开通 R2 必须先绑信用卡#

即使你全程一分钱不花,停留在免费额度内。

官方开通步骤原文是"进入 Dashboard → Storage & databases → R2 → Overview → Complete the checkout flow to add an R2 subscription"——这个 "checkout flow"(结账流程)本身就要录入支付信息。R2 是 Cloudflare 平台上少数几个"免费额度内也强制绑卡"的产品,Workers 和 Pages 都不用。

社区有人反馈开通时遇到过 $5 左右的预授权验证扣款(非实际计费),是否发生因账户风控而异。 (来源:https://developers.cloudflare.com/r2/get-started/)

⚠️ 坑二:r2.dev 公开域名只能开发用#

R2 桶要对外公开访问有两种方式:

  1. r2.dev 子域名:一键开启,但官方明确写着受速率限制仅供开发用途,不能配 WAF、不能配缓存策略、不能做访问控制。文档还专门警告"不要把 CNAME 指向 r2.dev,这是不受支持的用法"。
  2. 自定义域名:绑到你自己的域名上,走完整 CDN + 缓存 + WAF。生产必须用这个。 单桶最多挂 100 个自定义域名。

(来源:https://developers.cloudflare.com/r2/buckets/public-buckets/)

这条和第十章的域名问题是连着的——想正经用 R2 存图,你绕不开"域名得在 Cloudflare"这件事。

其他限制#

项目 限制
单对象大小 约 4.995 TiB
单次(非分片)上传 约 4.995 GiB
最大分片数 10,000
对象 key 长度 1,024 字节
同一对象并发写 每秒 1 次
REST API 速率 每 5 分钟 1,200 次
预签名 URL 最长有效期 7 天(604,800 秒)

预签名 URL(presigned URL) 值得单独说:这是让客户端(手机 App、浏览器)直接把文件 PUT 到 R2、不经过你的 Worker 中转的机制。Worker 只负责生成一个带签名的临时 URL 给客户端。

为什么重要:回想第一章那个 10ms CPU 限制——如果让用户把图片 POST 给 Worker、Worker 再转存 R2,光是处理 body 就可能超时。预签名 URL 让文件流量完全绕开 Worker。这是 Workers 上做文件上传的标准姿势,不是可选优化。

(来源:https://developers.cloudflare.com/r2/api/s3/presigned-urls/ · https://developers.cloudflare.com/r2/platform/limits/)


四、D1(SQLite 数据库)#

它是什么#

Cloudflare 托管的、基于 SQLite 的关系型数据库。你写标准 SQL,支持表、JOIN、事务、外键。

和 Supabase/Postgres 的关键差异:

  • 没有连接池概念,不用连接字符串,走 binding 调用(见第五章)
  • 单库本质单线程,同一时刻只处理一个查询。1ms 级的查询理论上能到 ~1000 QPS,100ms 级的查询就只有 ~10 QPS
  • 外键默认强制开启,且因为每条查询都在隐式事务里跑,你没法中途关掉,只能用 PRAGMA defer_foreign_keys 延迟检查
  • 支持 FTS5 全文检索和 JSON 函数。但注意:含虚拟表(FTS5 表)的数据库不支持导出
  • 没有 Postgres 的扩展生态,没有 pgvector、没有 PostGIS。要做向量检索得用 Vectorize

⚠️ 免费额度:博主说错的地方在这#

项目 Free Paid
账户 D1 总存储 5 GB 1 TB
单个数据库大小 500 MB 10 GB(这个上限不可申请提升
数据库个数 10 个 5 万个
每天读行数 500 万行 250 亿行
每天写行数 10 万行 5000 万行
单查询最长时长 30 秒
单行/字符串大小 2 MB
单条 SQL 长度 100 KB
每次调用最多查询数 50 条 1000 条
单表最大列数 100 列
Time Travel 回溯 7 天 30 天

博主说的"D1 给 5GB"指的是账户总量,实际约束你的是单库 500MB。另外他只提了读(500 万行),没提写只有 10 万行/天——超了当天直接对查询返回错误,UTC 零点重置。

(来源:https://developers.cloudflare.com/d1/platform/limits/ · https://developers.cloudflare.com/d1/platform/pricing/)

对个人项目来说 500MB 存元数据绰绰有余(一条物品记录几百字节,能存上百万条)。但别拿 D1 存文件

migrations 机制(博主第 3 条,这条他说得对)#

Shell
# 创建迁移文件,自动生成 migrations/0001_xxx.sql
wrangler d1 migrations create <数据库名> <迁移名>

# 看还有哪些没应用
wrangler d1 migrations list <数据库名>

# 应用到远程生产库
wrangler d1 migrations apply <数据库名>

# 应用到本地开发库(Miniflare 模拟)
wrangler d1 migrations apply <数据库名> --local

约定:迁移文件放项目根的 migrations/ 文件夹,文件名自带版本号前缀。配置里用 database_id(生产)和 preview_database_id(本地)区分两套库。

官方建议:命令里用数据库名称而不是 binding 名,因为名称创建后不可改,binding 名随时能改,用名称能避免误操作到错的库。

(来源:https://developers.cloudflare.com/d1/reference/migrations/)

只读副本#

D1 的全球只读副本 2025 年 4 月进入公开 Beta,通过 Sessions API(withSession())使用,不额外收费。截至 2026-07-31,官方文档正文里既没标 Beta 也没有明确的 GA 公告,是否已转正未查证——生产项目采用前建议自己再确认一次。


五、Bindings:为什么没有连接字符串#

这是 Cloudflare 和你熟悉的世界差别最大的一个设计,值得单独理解。

你现在的做法(Supabase / 任何数据库):

示意
.env 里放一个 DATABASE_URL="postgresql://user:password@host:5432/db"
代码里读这个环境变量,拿它去连

这个字符串本身就是钥匙。它一旦泄露——提交进 git、打进日志、被 SSRF 读走——攻击者在世界任何角落都能直接连上你的库。(你自己就踩过:media-studioDATABASE_URL 被覆盖成占位值那次。)

Cloudflare 的做法

你在 wrangler.jsonc声明"这个 Worker 可以访问哪个 R2 桶、哪个 D1 库":

JSONC
{
  "d1_databases": [
    { "binding": "DB", "database_name": "my-db", "database_id": "xxx" }
  ],
  "r2_buckets": [
    { "binding": "MY_BUCKET", "bucket_name": "my-bucket" }
  ]
}

部署后,运行时平台直接在 env 对象上挂出可调用的对象:env.DBenv.MY_BUCKET

代码里根本不存在任何密钥字符串。 权限是平台按"这个 Worker 实例"的身份注入的。即使你的代码全部泄露,攻击者拿到的也只是 env.MY_BUCKET.put() 这行字,没有任何能脱离 Cloudflare 环境单独使用的凭证。

这从机制上消灭了"密钥硬编码 / 误提交 / 日志泄露"这一整类问题。

(来源:https://developers.cloudflare.com/workers/runtime-apis/bindings/)

最小可运行示例(Hono + D1 + R2)#

TypeScript
import { Hono } from "hono";

// 类型对应 wrangler.jsonc 里声明的 binding 名
type Bindings = {
  DB: D1Database;
  MY_BUCKET: R2Bucket;
};

const app = new Hono<{ Bindings: Bindings }>();

// D1 查询:用绑定参数防注入,别拼字符串
app.get("/users/:id", async (c) => {
  const id = c.req.param("id");
  const { results } = await c.env.DB
    .prepare("SELECT id, name FROM users WHERE id = ?")
    .bind(id)
    .all();
  return c.json(results);
});

// R2 上传:直接把请求体流式写进桶,全程无密钥
app.put("/files/:key", async (c) => {
  const key = c.req.param("key");
  await c.env.MY_BUCKET.put(key, c.req.raw.body, {
    httpMetadata: {
      contentType: c.req.header("content-type") ?? "application/octet-stream",
    },
  });
  return c.json({ ok: true, key });
});

export default app;

六、Hono 是什么,为什么不是 Express#

Hono(日语"炎")是个极小的 Web 框架,零依赖,hono/tiny 预设压缩后不到 14KB。

为什么 Cloudflare 生态清一色用它:因为 Express 在 Workers 上根本跑不起来。Express 深度绑定 Node 的 http 模块(http.IncomingMessage / ServerResponse),而 Workers 没有 Node。

Hono 是纯粹基于 Web 标准(Fetch API 的 Request / Response)写的,所以同一套代码不改一行能跑在 Workers、Deno、Bun、Vercel Edge、Node.js 上。

最小例子:

TypeScript
import { Hono } from 'hono'
const app = new Hono()

app.get('/', (c) => c.text('Hello Cloudflare Workers!'))

export default app

起项目:npm create hono@latest,选 cloudflare-workers 模板。

(来源:https://hono.dev/docs/getting-started/cloudflare-workers)

如果你写过 Express,Hono 的 API 会非常眼熟——app.getapp.use、中间件洋葱模型都一样,主要区别是 handler 里拿到的是一个 context c,而不是 (req, res)


七、部署:wrangler、Workers Builds、Pages 之争#

wrangler#

官方 CLI,地位相当于你熟的 vercel CLI。管本地开发(wrangler dev)、发布(wrangler deploy)、管理 D1/KV/R2/Secrets。

配置文件新项目用 wrangler.jsonc(旧的是 wrangler.toml,字段等价):

JSONC
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-07-30",
  "compatibility_flags": ["nodejs_compat"],
  "observability": {
    "enabled": true,
    "head_sampling_rate": 1
  },
  "assets": {
    "directory": "./public",
    "binding": "ASSETS"
  },
  "d1_databases": [
    { "binding": "DB", "database_name": "my-db", "database_id": "xxx" }
  ],
  "r2_buckets": [
    { "binding": "MY_BUCKET", "bucket_name": "my-bucket" }
  ]
}

(来源:https://developers.cloudflare.com/workers/wrangler/configuration/)

Workers Builds(博主第 2 条)#

这就是"连 GitHub 后 push 自动部署"那个功能,官方叫 Workers Builds。连好仓库后 git push 触发构建,构建成功自动把新版本设为线上,还给 PR 提供预览 URL。功能成熟,文档页最近更新于 2026-07-03,未标 beta。

这个心智模型和你现在的 Vercel Git 集成完全一致,你那条"禁止手动部署、一律 push 触发"的规矩可以原样搬过来。

(来源:https://developers.cloudflare.com/workers/ci-cd/builds/)

Pages 还是 Workers?#

这里有个容易走错路的岔口。Cloudflare 历史上有两个托管产品:

  • Cloudflare Pages:早期的静态站/前端托管,对标 Vercel/Netlify
  • Workers + Static Assets:现在的方案,一个 Worker 同时托管静态文件和后端逻辑

现状:Pages 没下架,但官方明确让新项目走 Workers。Workers 团队负责人 Kenton Varda 的说法是"把 Pages 特有的功能逐个变成 Workers 的通用功能"。Pages 现存项目继续维护,但不会再拿到新特性

结论:新项目直接用 Workers + Static Assets,别碰 Pages。

另外别搞混:还有个叫 Workers Sites 的更古老方案,已在 Wrangler v4 里标记 deprecated,和现在推荐的 Static Assets 是两回事。

(来源:https://developers.cloudflare.com/workers/static-assets/ · https://developers.cloudflare.com/workers/wrangler/deprecations/)


八、Next.js 整站跑在 Workers 上(博主第 11 条)#

@opennextjs/cloudflare 适配器,把 Next.js 构建产物转换成 Workers 能跑的形态。社区维护,Cloudflare 官方文档背书。

当前状态(2026-07-31):npm 最新版 1.20.2,支持 Next.js 16 全部版本,以及 14、15 的最新 minor。Next.js 14 的支持原计划 2026 Q1 结束(已过),新项目实际只能用 15/16。

支持:App Router、Route Handlers、SSG、SSR、标准 Edge Middleware、ISR、PPR。

不支持 / 有坑

  • Next.js 15.2 引入的 Node Middleware 不支持
  • 图片优化默认用不了 Next 内置的,要额外接 Cloudflare Images 单独配
  • 体积是最现实的天花板:打包出来的 Worker 要满足 免费版 gzip 后 3MB / 付费版 10MB。官方特别强调是压缩后大小。一个带了 UI 库和若干依赖的真实 Next.js 站点很容易在免费版直接超限
  • Windows 本机构建不保证兼容,建议 WSL 或 Linux CI

(来源:https://opennext.js.org/cloudflare · https://www.npmjs.com/package/@opennextjs/cloudflare)

现实评估:演示级别的轻量 Next.js 站在免费版能跑。但只要涉及图片优化、较重依赖、或 CPU 密集的 SSR,3MB 体积 + 10ms CPU 这两条免费版硬限制会很快顶到,通常得上 $5/月的 Workers Paid。

给你的建议:第一个练手项目别一上来就 OpenNext。前端先用 Workers Static Assets 托管一个 Vite SPA,和 Hono API 打包在同一个 Worker 里,一次部署、体积可控、零踩坑。等主流程跑通了,想练 OpenNext 再单开一个 Worker 试。


九、邮件(坑最多的一章)#

博主第 6、7 条说"Worker 里直接把邮件发出去,第三方邮件服务都不用接"。这里的实际情况分三段历史,很容易看到过时信息:

第一段:MailChannels 时代(已终结)#

MailChannels 曾经给 Workers 免费提供发信通道,是社区标配的白嫖方案。2024 年 6 月 30 日免费服务正式终止。 网上大量 2023 年前的教程都是基于它写的,全部作废

MailChannels 现在转型成独立 Email API,有个每天 100 封的免费计划。

(来源:https://blog.mailchannels.com/important-update-mailchannels-email-sending-api-for-cloudflare-workers-to-be-terminated/)

第二段:只能收不能发(2024~2025)#

Email Routing 是 Cloudflare 的老功能:把 xxx@你的域名 收到的邮件转发到你的真实邮箱。免费、无限量。但它只管收,不管发。

这就是网上很多文章说"Cloudflare 只能收信"的由来。

第三段:现在(2026)能发了,但免费版不行#

Cloudflare Email Service2026-04-16 正式进入公测(Public Beta)。官方原话:"Email Sending graduates from private beta to public beta, and you can now send transactional emails directly from Workers with a native Workers binding — no API keys, no secrets management."

配置:

JSONC
{ "send_email": [{ "name": "EMAIL" }] }
TypeScript
await env.EMAIL.send({
  to: "recipient@example.com",
  from: "welcome@yourdomain.com",
  subject: "Welcome!",
  html: "<h1>Welcome!</h1>",
  text: "Welcome!",
});

但有三条限制

  1. 💰 发给「任意收件人」必须 Workers Paid($5/月起),每月 3,000 封免费,超出 $0.35/千封。定价表里 Workers Free 那一栏的"出站发信"直接写着 Not available
  2. 发信域名验证是两阶段的:域名走完 DNS 验证(onboard)之前,只能发给你账户里已验证过的目标地址;onboard 之后才能发给任意陌生收件人。
  3. 单次发送 to + cc + bcc 总数不超过 50 个,不适合群发。

⚠️ 2026-07-31 订正(本文初稿写错了,这里是重点)

初稿断言「免费版根本没有出站发信能力」,这条不准确。官方 Limits 页原文:

Sends to verified destination addresses are always free: they do not count toward your monthly quota or your daily sending limits, on any plan, including when only Email Routing is configured. You can only send from your routing domains.

也就是说,Workers 免费版可以用 env.EMAIL.send() 发邮件,只要收件人是你账号里已验证的目标地址(Destination Address),而且这类发送不计入配额、不计入日限额。定价表写的 "Not available" 指的是「发给任意陌生收件人」这个能力。

两个前提别漏:发件域必须是你已配置 Email Routing 的域;收件地址必须先在 Dashboard → Email Service → Email Routing → Destination Addresses 里加过并点邮件里的链接验证过(每账号上限 200 个)。

这对你意义很大:你现在所有站的通知邮件(daily-digest 日报、ai-daily、额度提醒)收件人都是你自己那个 Gmail —— 这个场景免费版就能跑,白嫖,不用 $5/月,也不用 Resend。只有发给外人(比如以后做的产品要给用户发验证码)才需要付费。

(来源:https://blog.cloudflare.com/email-for-agents/ · https://developers.cloudflare.com/email-service/platform/limits/ · https://developers.cloudflare.com/email-service/platform/pricing/ · https://developers.cloudflare.com/email-service/configuration/email-routing-addresses/)

所以博主第 7 条"第三方邮件服务都不用接"——分情况#

三条路:

方案 成本 适合
CF Email Service · 发给已验证地址 完全免费,所有 plan,不计配额 自用通知的最优解:日报、告警、部署提醒这类只发给自己的邮件
CF Email Service · 发给任意收件人 Workers Paid $5/月,含 3000 封 已经付费、想零第三方依赖。注意还在公测,别放关键链路
Resend 免费 3000 封/月、100 封/天、1 个域名、数据留 30 天 免费版里发给陌生收件人的现实选择,文档最全
AWS SES ~$0.10/千封 成本敏感,但要自己写 SigV4 签名

对你来说 Resend 是现成的——你的 saveme505.help 已经在 Resend 验证过,claude@saveme505.help 能发任意收件人。Worker 里调它就一个 fetch POST 到 https://api.resend.com/emails,不用装 SDK(多数邮件 SDK 假设 Node 环境,Workers 里跳过 SDK 更轻)。API key 用 wrangler secret put 存。

注意 Resend 默认限流 2 请求/秒,量大要配 Cloudflare Queues 削峰。

Cloudflare 官方有专门教程:https://developers.cloudflare.com/workers/tutorials/send-emails-with-resend/


十、域名(第二个硬钉子)#

博主第 7、8 条说"域名绑定一次全子域共用,每个子域绑独立 Worker,SSL 全自动"。这是对的,但有一个他没说的硬前提

Worker 的三种访问方式#

  1. *.workers.dev:账户自带的免费子域。官方定位是"个人/业余项目,不适合业务关键场景"。
  2. Custom Domain:把 Worker 直接绑成某个域名的源站,DNS 记录和 SSL 证书全自动创建。
  3. Route:把 Worker 挂在一个已经走 Cloudflare 代理的现存主机名前面,当反向代理/中间层用。

⚠️ 硬前提:Custom Domain 必须把域名 NS 托管到 Cloudflare#

官方前置条件原文:"To add a Custom Domain, you must have an active Cloudflare zone." 排除条款:"You cannot create a Custom Domain on a hostname with an existing CNAME DNS record or on a zone you do not own."

能不能只加个 CNAME、NS 留在别家?不能。

Cloudflare 确实有一种叫 "Partial (CNAME) Setup" 的接入方式,但仅限 Business / Enterprise 付费套餐(Free 和 Pro 都是 No),而且结合"已有 CNAME 记录不能建 Custom Domain"这条,此路不通。

Cloudflare for SaaS / Custom Hostnames 也帮不上——那是给 SaaS 提供方让客户的域名指向自己用的,前提也是你自己得先有完整 zone。

(来源:https://developers.cloudflare.com/workers/configuration/routing/custom-domains/ · https://developers.cloudflare.com/dns/zone-setups/partial-setup/)

这对你意味着什么#

你的 saveme505.help 现在 NS 在 ns1/ns2.vercel-dns.com,上面挂着十几个 Vercel 生产站。要给 Worker 绑这个域名的子域,必须把整个域名的 NS 迁到 Cloudflare,然后把现有那十几条 Vercel 站点的解析记录逐条在 Cloudflare 重建。迁移本身免费版就行,不需要付费套餐,但迁移期间有解析中断风险。

三条路,按风险排序:

  1. 先用 *.workers.dev:零成本零风险,练手够用。缺点是国内访问不稳定(社区长期反馈,但能查到的具体帖子多是 2022-2023 年的,2025-2026 年的新反馈未查证)。
  2. 新买一个域名放 Cloudflare:约 ¥70/年,NS 直接托管在 CF,和 saveme505.help 完全隔离互不影响。想把博主那套"一域多子域"玩全,这是最干净的路。
  3. saveme505.help 到 Cloudflare:能统一管理,还顺便能给静态站上 CF Access 密码门(你之前记过 Vercel hobby 版做不到这个)。但迁移风险最大,不建议为练手做。

SSL 证书#

  • Custom Domain 创建时自动签发证书,无需手动申请。但删除 Custom Domain 时证书不会自动删,要手动去 Dashboard 清理。
  • Universal SSL(免费默认证书)只覆盖根域名 + 一级子域。像 api.staging.example.com 这种多级子域不覆盖
  • 要覆盖多级子域或通配符得上 Advanced Certificate,一张证书最多 50 个 SAN(根域占 1 个,剩约 49 个)。
  • 一个域名下挂多个一级子域、各绑不同 Worker:免费版 Universal SSL 就自动覆盖,博主这条说得对。

十一、日志与可观测性(博主第 9、10 条)#

配置里加一行就开:

JSONC
"observability": {
  "enabled": true,
  "head_sampling_rate": 1
}

不开的话,日志只在你手动 wrangler tail 的那一刻能看到,不会留存

Workers Logs(后台的结构化日志查询):

  • Free:20 万条/天,保留 3 天
  • Paid:月包 2000 万条(超出 $0.60/百万),保留 7 天
  • 想留更久要接 Logpush 导出到 R2 或第三方

wrangler tail:命令行实时日志流,免费,但不落盘,等价于 vercel logs --follow

head_sampling_rate 设 1 是 100% 记录,高流量时可以调低(比如 0.1)省额度。

(来源:https://developers.cloudflare.com/workers/observability/logs/workers-logs/)


十二、免费额度总表(一页速查)#

产品 免费额度 超了会怎样
Workers 10 万请求/天;CPU 10ms/请求;脚本 3MB gzip;内存 128MB 拒绝服务
D1 账户共 5GB;单库 500MB;10 个库;500 万行读/天;10 万行写/天 查询直接报错,UTC 零点重置
R2 10GB 存储;Class A 100 万/月;Class B 1000 万/月;出口永久免费 需绑卡才能开通
KV 读 10 万/天;写 1000/天;1GB 存储 硬性拒绝
Durable Objects 10 万请求/天;13,000 GB-秒;5GB SQLite 2025-04 起才有免费层
Workers Logs 20 万条/天,留 3 天
Email 出站 发给已验证目标地址:免费且不计配额;发给任意收件人:免费版不可用 要发给陌生人得上 Paid 或用 Resend
Email Routing 入站 无限免费

升级到 Workers Paid 是 $5/月起,解锁:CPU 30 秒(可配 5 分钟)、脚本 10MB、Email 出站 3000 封/月、日志留 7 天。对认真做的项目,这 $5 基本是必花的。


十三、和你现有技术栈的映射#

你现在用的 Cloudflare 对应 换过去的得失
Vercel Serverless Functions Workers 得:冷启动近 0、国内延迟低。失:10ms CPU、不是 Node
Vercel(Next.js 托管) Workers + OpenNext 得:省一层平台。失:3MB 体积墙、图片优化要另配
Supabase / Postgres D1 得:零运维、边缘就近。失:单库 500MB、单线程写、无扩展生态
Vercel Blob R2 得:出口永久免费、S3 API 兼容。失:要绑卡、公开访问要自定义域名
Vercel Cron Cron Triggers 免费版 5 个
Resend Resend(继续用) 或 CF Email Service(要 $5/月) 你域名已验证,继续用 Resend 最省事
Vercel 环境变量 + 连接串 Bindings 得:代码里没有密钥可泄露
—— Durable Objects 你现在没有对应物。要做实时协作/WebSocket 房间/强一致计数器时用它
想留着 Supabase 不搬 Hyperdrive 在边缘加速访问现有 Postgres,代码不用重写

十四、ShipSwift 到底给了什么(实测)#

你已经 fork 并 clone 到 ~/work/ShipSwift。视频里说"全家桶配置集成方案已经开源到 ShipSwift",实际核实结果:

公开仓库里没有 Cloudflare#

本地全库 grep cloudflare / workers / hono / d1 / r2 —— 零命中。 线上核实 README 和 signerlabs/shipswift-skills 仓库 —— 也未出现任何 Cloudflare 字样。

它自述的技术栈是 SwiftUI、StoreKit 2、Amplify SDK(AWS)、AVFoundation、Vision、SpriteKit、VolcEngine ASR。官网 shipswift.app 明确写的是 "Full-stack iOS app + AWS backend",只在一个案例站点 "PawPort Boarding" 的技术栈列表里出现过一次 "cloudflare workers"。

更能说明问题的是:仓库里那个 SWAuth 认证模块,用的是 AWS Amplify + Cognito,不是 Cloudflare。

结论:ShipSwift 免费开源的是纯 iOS 客户端组件(MIT)。所谓"全家桶集成方案"在 Pro 付费层($89 一次性,含 "Backend implementation" / "Deployment Guides"),需登录才能看,公开渠道查不到任何 wrangler 配置、Hono 路由或 D1 schema。视频里那句话,要么指的是 Pro 内容,要么就是宣传口径。

但它的 iOS 组件确实能用,而且有个正好对你胃口的#

ShipSwift/SWPackage/SWModule/ 下有:

模块 内容
SWSubjectLifting 主体抠图(去背景) —— 就是你说的"Vision 提取图片中的物体"
SWCamera 相机 + 人脸关键点
SWAuth 登录(AWS Cognito,用不上,但 UI 能抄)
SWPaywall StoreKit 2 内购
SWChat 聊天 UI + 火山引擎 ASR
SWSetting 设置页

外加 40 个动画组件、9 个图表组件、22 个 UI 组件。全是 MIT,自包含,复制文件就能用。

SWSubjectLifting 的实现值得说一下——它走的是 VisionKit 路线,不是 Vision 框架:

Swift
private let analyzer = ImageAnalyzer()
private let configuration = ImageAnalyzer.Configuration(.visualLookUp)
private var interaction: ImageAnalysisInteraction   // preferredInteractionTypes = .imageSubject

// 核心三步
let analysis = try await analyzer.analyze(image, configuration: configuration)
interaction.analysis = analysis
let subjects = await interaction.subjects
let subjectImage = try await subjects.first!.image   // 已去背景
let pngData = subjectImage.pngData()

iOS 17+,代码量极小。

抠图有两条路,可以对比着练

VisionKit ImageAnalyzer Vision GenerateForegroundInstanceMaskRequest
代码量 很少(上面这几行) 多一些,要自己处理 mask 合成
拿到的东西 直接给你去背景的 UIImage 给你每个实例的独立 mask,可控性强
多物体 subjects 数组,但 API 偏"整体" 天然按实例分离,能选择性抠某一个
适合 快速出效果 想深入理解 Vision 管线

你机器上是 Xcode 26.5 / Swift 6.3 / iOS 26.5 SDK,两条路都支持。

关于 iOS + Cloudflare 的参考资料#

调研下来,没有现成的、公开可核实的"iOS + Cloudflare 全家桶"完整模板。能用的零件:

  • Sign in with Apple → Worker 验证nora-soderlund/cloudflare-apple-sign-in(小仓库,6 星,维护活跃度未查证),或者自己用 jose / cloudflare-worker-jwt 校验 Apple identity token 的 JWKS 签名
  • iOS 端直传 R2:没找到专门的 Swift 教程,但原理上零障碍——预签名 URL 本质就是一个标准 HTTP PUT,Swift 端 URLSession 直接 PUT 上去即可,不需要任何 SDK

也就是说这块得自己拼,但拼起来并不难。


十五、新手踩坑清单(照这个顺序避雷)#

  1. 别在 Worker 里做重计算 —— 免费版 10ms CPU。图片处理、加解密这类活放客户端,或者放 Queue 异步做。
  2. 文件上传一定走预签名 URL —— 别让文件流经 Worker。
  3. 开 R2 前准备好一张卡 —— 免费额度内也要绑。
  4. 别拿 KV 当数据库 —— 最终一致,跨地区最多 60 秒延迟,做计数器会算错。
  5. D1 盯着写入额度 —— 免费版每天只有 10 万行写,比读少 50 倍。
  6. 单库 500MB 上限,别往 D1 塞文件 —— 单行上限也才 2MB。
  7. 想绑自定义域名,先确认域名 NS 在 Cloudflare —— 这事没有变通办法。
  8. 生产别用 r2.dev 公开桶 —— 官方明说限速、只给开发用。
  9. 免费版发不了给陌生人的邮件 —— 但发给账号内已验证的目标地址(比如你自己邮箱)在所有 plan 上都免费、且不计配额,自用通知白嫖即可;发给外人才要 $5/月 或接 Resend。
  10. 新项目用 Workers + Static Assets,不用 Pages —— Pages 只维护不进化了。
  11. compatibility_date 写当天日期并定期更新 —— 它决定运行时行为,写太旧拿不到新特性和修复。
  12. Next.js 整站上 Workers 先量体积 —— 免费版 gzip 后 3MB 是硬墙。
  13. observability.enabled 记得开 —— 不开日志不留存,出问题查不了。
  14. npm 包先确认能不能在 Workers 跑 —— 带原生二进制的(sharp、bcrypt 原生版)一律不行。

附:所有来源#

Workers 与部署

存储

邮件与域名

日志

ShipSwift


明确"未查证"的点#

  • isolate 内存开销"约 2MB vs 容器 30-50MB"这个具体数字来自第三方博客,量级可信但官方未逐字确认
  • D1 只读副本 / Sessions API 是否已脱离 Beta 转 GA,官方页面无明确标签
  • R2 开通时是否一定发生 $5 预授权扣款,因账户风控而异
  • *.workers.dev 在国内的可访问性,能查到的具体反馈多为 2022-2023 年,2025-2026 年的新数据未查证
  • MailChannels 2026 年的付费定价具体数字