
multi-tenant-architecture
为 Cloudflare 或 Vercel 上的多租户 SaaS 平台提供架构指导。涵盖平台选择、域名策略、租户识别与隔离、子域名路由、自定义域名与 SSL、白标设置、租户上下文传播、PSL 提交,以及将平台限制映射到定价计划。在构建多租户应用或询问“如何支持多个租户”、“构建白标平台”、“添加自定义域名”、“按子域名路由租户”或“将限制映射到计划”时使用。对于一般的应用文件夹结构,请使用 codebase-architecture;对于搭建新的 Next.js 仓库,请使用 scaffold-nextjs。
92Star
8Fork
更新于 2026/8/23
SKILL.md
只读
名称
multi-tenant-architecture
描述
为 Cloudflare 或 Vercel 上的多租户 SaaS 平台提供架构指导。涵盖平台选择、域名策略、租户识别与隔离、子域名路由、自定义域名与 SSL、白标设置、租户上下文传播、PSL 提交,以及将平台限制映射到定价计划。在构建多租户应用或询问“如何支持多个租户”、“构建白标平台”、“添加自定义域名”、“按子域名路由租户”或“将限制映射到计划”时使用。对于一般的应用文件夹结构,请使用 codebase-architecture;对于搭建新的 Next.js 仓库,请使用 scaffold-nextjs。
多租户平台架构(Cloudflare 或 Vercel)
- 适用场景: 域名策略、租户识别与隔离、子域名路由、自定义域名、白标设置,以及在 Cloudflare 或 Vercel 上的计划/限制映射。
- 不适用场景: 一般的应用文件夹结构或模块边界(使用
codebase-architecture)、搭建新仓库(使用scaffold-nextjs),或当路由动态提供每个租户的 SEO 工件内容时:站点地图条目、规范 URL、结构化数据、索引策略(使用optimise-seo)。
目录
- 平台选择(先决定)
- 工作流程(顺序很重要)
- 注意事项
- 输出模式
- 提交前检查清单
- 相关技能
平台选择(先决定)
| 信号 | 平台 | 加载 |
|---|---|---|
| 租户运行不受信任或每租户代码;需要代码级隔离;边缘优先计算在 D1/KV/Durable Objects 上 | Cloudflare(Workers for Platforms,dispatch namespaces) | cloudflare-platform.md |
| 所有租户共享一个 Next.js 代码库;需要 ISR、React Server Components、托管部署 | Vercel(App Router + Middleware) | vercel-platform.md,然后是 vercel-domains.md 用于域名 |
- 选择一个平台并坚持;切勿混合托管(混合路由复杂性会成倍增加)。
- 除非明确比较,否则仅加载所选平台的参考。
- 在决定域名策略(步骤 1)时加载 psl.md。
- 在将限制映射到定价(步骤 8)之前加载 limits-and-quotas.md。
agents/openai.yaml是外部运行器的启动器元数据;正常使用中不要加载它。
工作流程(顺序很重要)
复制此清单以跟踪进度:
多租户进度:
- [ ] 步骤 1:域名策略和 PSL 决策
- [ ] 步骤 2:租户识别策略
- [ ] 步骤 3:隔离模型
- [ ] 步骤 4:确定性路由
- [ ] 步骤 5:租户上下文传播
- [ ] 步骤 6:最小权限绑定和租户配置
- [ ] 步骤 7:自定义域名和每租户静态文件
- [ ] 步骤 8:限制映射到计划
- [ ] 步骤 9:API 与 UI 对齐
- [ ] 步骤 10:扩展模式
- 选择域名策略
- 专用租户域名,与品牌域名分开,用于所有子域名和自定义主机名。声誉不会隔离:
random.acme.com上的钓鱼网站会损害整个域名。 - 为租户工作负载注册单独的 TLD(例如,租户用
acme.app,品牌用acme.com)。 - 兄弟子域名上的不受信任内容:选择 PSL 提交,记录所有者加时间线。否则记录
No PSL并注明 cookie 隔离原因。参见 psl.md。 - 尽早开始 PSL;审核需要数周。
- 选择租户识别策略(选择一个主要策略;提供自定义域名作为升级路径)
- 基于子域名:
tenant.yourdomain.com。需要通配符 DNS。在规模上最简单。 - 自定义域名:租户将其自己的域名 CNAME 到您的平台。最适合严肃/付费租户。
- 基于路径:
yourdomain.com/tenant-slug。无需每租户 DNS/SSL,但限制品牌并复杂化 cookie 隔离。
- 定义隔离模型
- Cloudflare:通过 dispatch namespaces 为不受信任的代码提供每租户 Workers。除非您完全控制代码和数据,否则避免共享租户分支。
- Vercel:共享 Next.js 应用,使用
tenant_id作用域。中间件从主机名解析租户;每个查询都包含租户上下文。Postgres RLS 用于纵深防御。
- 确定性路由流量(租户永远不能控制路由或看到彼此)
- Cloudflare:平台 Worker 拥有路由:主机名 -> 租户 ID -> dispatch namespace -> 租户 Worker。无映射时返回 404。
- Vercel:中间件提取主机名,重写到
/domains/[domain]段;Edge Config 用于亚毫秒级查找。无映射时返回 404。
- 通过堆栈传递租户上下文(单一权威:中间件或平台 Worker;永远不要信任客户端提供的身份)
- Cloudflare:平台 Worker 解析租户,在分派到租户 Worker 之前注入标头/绑定。
- Vercel:中间件在转发的请求标头(不是响应)上设置
x-tenant-id、x-tenant-slug、x-tenant-plan。服务器组件通过headers()读取;API 路由从请求标头读取。实现在 vercel-platform.md 中。
- 仅绑定所需内容
- Cloudflare:每个租户的最小权限绑定(数据库/存储/受限平台 API),无共享全局状态。新绑定是显式更改;重新部署以授予访问权限。
- Vercel:Edge Config 用于租户配置(域名映射、功能标志、计划信息)。
@vercel/sdk用于域名管理。数据库连接按tenant_id作用域,或每租户数据库(Neon)。
- 支持自定义域名和每租户静态文件
- 提供 DNS 目标,验证所有权,存储映射,按主机名路由。
- Cloudflare:Cloudflare for SaaS 自定义主机名,带托管证书。参见 cloudflare-platform.md。
- Vercel:
@vercel/sdk用于域名 CRUD 以及自动 Let's Encrypt SSL;通配符子域名需要 Vercel 名称服务器。参见 vercel-domains.md。 - 自定义域名将声誉转移到租户,并创建自然的用户细分(休闲用户在平台域名上,严肃用户在自己的域名上)。
robots.txt、sitemap.xml、llms.txt必须因租户而异;切勿从/public提供。Cloudflare:在租户 Worker 中生成。Vercel:在域名段下的路由处理程序(参见 vercel-platform.md)。
- 将限制作为计划呈现
- 将平台限制映射到定价层级;在 API 和 UI 中公开。
- 请求中不要有长时间运行的任务;使用队列或工作流。
- 参见 limits-and-quotas.md;在最终架构或定价决策前重新检查官方文档。
- 让 API 成为产品
- 一切通过 HTTP 工作;UI 用于操作、事件、计费。
- 平台逻辑保留在路由层(dispatch Worker 或 Middleware);租户内容服务请求。
- 如果它只在 UI 中工作,平台就在泄漏。
- 在不破坏边界的情况下扩展
- 添加队列、工作流或容器作为可选模式。保持路由显式且隔离完整。
注意事项
- 租户标头放在中间件请求上,而不是响应上:服务器组件中的
headers()读取转发的请求标头,因此使用NextResponse.next({ request: { headers } }),否则租户 ID 永远不会到达。 - 如果自定义域名在路线图上,不要开始基于路径:以后迁移意味着 URL 重写、cookie 更改和 DNS 迁移。
- 切勿在没有 RLS 或
tenant_id作用域的情况下跨租户共享数据库连接:一个缺失的 WHERE 子句会泄漏另一个租户的数据。 - 切勿使用中间件或重定向阻止
/.well-known/acme-challenge/*:Let's Encrypt HTTP-01 验证失败,自定义域名 SSL 永远不会签发。 - Edge Config 写入不是即时的:传播需要长达 10 秒,因此读取 Edge Config 的“域名已连接”UI 会立即显示过时状态。
输出模式
# 多租户架构
## 平台决策
- 平台:Cloudflare | Vercel
- 选择此平台的原因:
- 被拒绝的平台及原因:
## 域名映射
- 品牌域名:
- 租户域名:
- 租户子域名:
- 自定义域名:
- PSL 决策:提交 | 无 PSL
- PSL 所有者/时间线或无 PSL 原因:
## 路由矩阵
| 主机模式 | 解析器 | 目标 | 未知租户行为 |
|---|---|---|---|
## 租户上下文流
- 权威:中间件 | 平台 Worker
- 传播:
- 服务器读取路径:
- 数据库/API 作用域:
## 隔离模型
- 计算隔离:
- 数据隔离:
- 配置/绑定隔离:
## 自定义域名生命周期
1. DNS 目标:
2. 所有权验证:
3. 证书配置:
4. 路由激活:
5. 移除/失败路径:
## 限制到计划表
| 限制 | 来源 URL/日期 | 免费 | 专业 | 企业 | 执行点 |
|---|---|---:|---:|---:|---|
## 验证证据
| 检查 | 命令/来源 | 预期结果 | 结果 |
|---|---|---|---|
提交前检查清单
- [ ] 平台已选择并记录理由
- [ ] 租户工作负载离开品牌域名;PSL 决策和时间线已设置
- [ ] 租户识别策略已选择;自定义域名升级路径已定义
- [ ] 隔离模型已定义:每租户 Workers(Cloudflare)或共享应用加 RLS(Vercel)
- [ ] 路由权威且租户不可见;dispatch 或 Middleware 处理所有流量
- [ ] 租户上下文仅通过 Middleware/平台 Worker 流动;不信任客户端提供的身份
- [ ] 自定义域名入门已定义:DNS 目标、验证、证书配置
- [ ] 每租户静态文件(robots.txt、sitemap.xml、llms.txt)动态提供
- [ ] 限制与计费挂钩;API 与 UI 对齐
- [ ] 限制快照从官方文档刷新并在规划说明中注明日期
证据命令(运行或标记 N/A):
| 检查 | 证据 |
|---|---|
| 租户上下文存在于边界 | `rg "x-tenant-id |
| 租户路由有效 | curl -sI -H "Host: tenant.example.com" <local-or-preview-url> |
| 每租户静态文件是动态的 | curl -s -H "Host: tenant.example.com" <url>/robots.txt 和 curl -s -H "Host: tenant.example.com" <url>/sitemap.xml |
| 自定义域名验证路径存在 | 计划中的 API 路由、SDK 调用或平台配置路径 |
| 平台限制是最新的 | 官方 Cloudflare/Vercel URL 及访问日期在限制到计划表中 |
相关技能
codebase-architecture:应用本身的文件夹结构、模块契约和中间件管道。scaffold-nextjs:在应用这些租户模式之前引导 Next.js turborepo。optimise-seo:路由工作后,每租户站点地图、规范 URL 和结构化数据。



