multi-tenant-architecture

multi-tenant-architecture

提供在 Cloudflare 或 Vercel 上建置多租戶 SaaS 平台的架構指引。涵蓋平台選擇、網域策略、租戶識別與隔離、子網域路由、自訂網域與 SSL、白標設定、租戶上下文傳遞、PSL 提交,以及將平台限制對應到定價方案。適用於建置多租戶應用程式,或詢問「如何支援多個租戶」、「建置白標平台」、「新增自訂網域」、「依子網域路由租戶」或「將限制對應到方案」時。一般應用程式資料夾結構請使用 codebase-architecture;建立新的 Next.js 儲存庫請使用 scaffold-nextjs。

92星標
8分支
更新於 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 產物內容:sitemap 條目、canonical 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:擴充模式
  1. 選擇網域策略
  • 專用租戶網域,與品牌網域分開,用於所有子網域和自訂主機名稱。信譽不會隔離:random.acme.com 上的釣魚網站會損害整個網域。
  • 為租戶工作負載註冊單獨的 TLD(例如租戶用 acme.app,品牌用 acme.com)。
  • 兄弟子網域上的不受信任內容:選擇 PSL 提交,記錄擁有者與時間表。否則記錄 No PSL 並附上 cookie 隔離原因。請參閱 psl.md
  • 儘早開始 PSL;審查需要數週。
  1. 選擇租戶識別策略(選擇一個主要策略;提供自訂網域作為升級路徑)
  • 子網域型tenant.yourdomain.com。需要萬用字元 DNS。大規模最簡單。
  • 自訂網域:租戶將自己的網域 CNAME 到您的平台。最適合付費/重要租戶。
  • 路徑型yourdomain.com/tenant-slug。無需每個租戶的 DNS/SSL,但限制品牌形象並使 cookie 隔離複雜化。
  1. 定義隔離模型
  • Cloudflare:透過 dispatch namespaces 為不受信任的程式碼使用每個租戶的 Workers。除非您完全控制程式碼和資料,否則避免共用租戶分支。
  • Vercel:共用 Next.js 應用程式,以 tenant_id 範圍界定。Middleware 從主機名稱解析租戶;每個查詢都包含租戶上下文。Postgres RLS 作為縱深防禦。
  1. 確定性路由流量(租戶絕不控制路由或看到彼此)
  • Cloudflare:平台 Worker 擁有路由:主機名稱 -> 租戶 ID -> dispatch namespace -> 租戶 Worker。沒有對應時回傳 404。
  • Vercel:Middleware 提取主機名稱,重寫到 /domains/[domain] 區段;Edge Config 用於次毫秒查詢。沒有對應時回傳 404。
  1. 透過堆疊傳遞租戶上下文(單一權威:Middleware 或平台 Worker;絕不信任用戶端提供的身份)
  • Cloudflare:平台 Worker 解析租戶,在分派到租戶 Worker 之前注入標頭/綁定。
  • Vercel:Middleware 在轉發的請求標頭(而非回應)上設定 x-tenant-idx-tenant-slugx-tenant-plan。Server Components 透過 headers() 讀取;API 路由從請求標頭讀取。實作請參閱 vercel-platform.md
  1. 只綁定需要的
  • Cloudflare:每個租戶的最小權限綁定(DB/儲存/有限平台 API),沒有共用的全域狀態。新綁定是明確的變更;重新部署以授予存取權。
  • Vercel:Edge Config 用於租戶設定(網域對應、功能旗標、方案資訊)。@vercel/sdk 用於網域管理。DB 連線以 tenant_id 範圍界定,或每個租戶一個資料庫(Neon)。
  1. 支援自訂網域與每個租戶的靜態檔案
  • 提供 DNS 目標、驗證所有權、儲存對應、依主機名稱路由。
  • Cloudflare:Cloudflare for SaaS 自訂主機名稱,搭配受管憑證。請參閱 cloudflare-platform.md
  • Vercel@vercel/sdk 用於網域 CRUD,加上自動 Let's Encrypt SSL;萬用字元子網域需要 Vercel 名稱伺服器。請參閱 vercel-domains.md
  • 自訂網域將信譽轉移到租戶,並建立自然的用戶區隔(休閒用戶在平台網域,重要用戶在自己的網域)。
  • robots.txtsitemap.xmlllms.txt 必須依租戶而異;絕不從 /public 提供。Cloudflare:在租戶 Worker 中產生。Vercel:在網域區段下的路由處理器(請參閱 vercel-platform.md)。
  1. 將限制呈現為方案
  • 將平台限制對應到定價層級;在 API 和 UI 中公開。
  • 請求中不要有長時間執行的工作;使用佇列或工作流程。
  • 請參閱 limits-and-quotas.md;在最終架構或定價決定前,重新檢查官方文件。
  1. 讓 API 成為產品
  • 所有功能都透過 HTTP 運作;UI 用於營運、事件、帳單。
  • 平台邏輯保留在路由層(dispatch Worker 或 Middleware);租戶內容服務請求。
  • 如果只能在 UI 中運作,表示平台正在洩漏。
  1. 在不破壞邊界的情況下擴充
  • 新增佇列、工作流程或容器作為選用模式。保持路由明確且隔離完整。

注意事項

  • 租戶標頭放在 Middleware 請求上,而非回應:Server Components 中的 headers() 讀取轉發的請求標頭,因此請使用 NextResponse.next({ request: { headers } }),否則租戶 ID 永遠不會到達。
  • 如果自訂網域在路線圖上,不要從路徑型開始:之後遷移意味著 URL 重寫、cookie 變更和 DNS 遷移。
  • 絕不在沒有 RLS 或 tenant_id 範圍界定的情況下跨租戶共用 DB 連線:缺少一個 WHERE 子句就會洩漏另一個租戶的資料。
  • 絕不要用 Middleware 或重新導向封鎖 /.well-known/acme-challenge/*:Let's Encrypt HTTP-01 驗證會失敗,自訂網域 SSL 永遠不會發出。
  • Edge Config 寫入不是即時的:傳播最多需要 10 秒,因此立即讀取 Edge Config 的「網域已連線」UI 會顯示過時狀態。

輸出結構

# 多租戶架構

## 平台決定
- 平台:Cloudflare | Vercel
- 選擇此平台的原因:
- 被拒絕的平台與原因:

## 網域對應
- 品牌網域:
- 租戶網域:
- 租戶子網域:
- 自訂網域:
- PSL 決定:提交 | 不提交
- PSL 擁有者/時間表或不提交原因:

## 路由矩陣
| 主機模式 | 解析器 | 目的地 | 未知租戶行為 |
|---|---|---|---|

## 租戶上下文流程
- 權威:Middleware | 平台 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.txtcurl -s -H "Host: tenant.example.com" <url>/sitemap.xml
自訂網域驗證路徑存在 計畫中的 API 路由、SDK 呼叫或平台設定路徑
平台限制是最新的 官方 Cloudflare/Vercel URL,附上 Limits-to-plan 表中的存取日期

相關技能

  • codebase-architecture:應用程式本身的資料夾結構、模組契約和中間件管線。
  • scaffold-nextjs:在套用這些租戶模式之前,先建立 Next.js turborepo。
  • optimise-seo:路由正常運作後的每個租戶 sitemap、canonical URL 和結構化資料。