
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:擴充模式
- 選擇網域策略
- 專用租戶網域,與品牌網域分開,用於所有子網域和自訂主機名稱。信譽不會隔離:
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範圍界定。Middleware 從主機名稱解析租戶;每個查詢都包含租戶上下文。Postgres RLS 作為縱深防禦。
- 確定性路由流量(租戶絕不控制路由或看到彼此)
- Cloudflare:平台 Worker 擁有路由:主機名稱 -> 租戶 ID -> dispatch namespace -> 租戶 Worker。沒有對應時回傳 404。
- Vercel:Middleware 提取主機名稱,重寫到
/domains/[domain]區段;Edge Config 用於次毫秒查詢。沒有對應時回傳 404。
- 透過堆疊傳遞租戶上下文(單一權威:Middleware 或平台 Worker;絕不信任用戶端提供的身份)
- Cloudflare:平台 Worker 解析租戶,在分派到租戶 Worker 之前注入標頭/綁定。
- Vercel:Middleware 在轉發的請求標頭(而非回應)上設定
x-tenant-id、x-tenant-slug、x-tenant-plan。Server Components 透過headers()讀取;API 路由從請求標頭讀取。實作請參閱 vercel-platform.md。
- 只綁定需要的
- Cloudflare:每個租戶的最小權限綁定(DB/儲存/有限平台 API),沒有共用的全域狀態。新綁定是明確的變更;重新部署以授予存取權。
- Vercel:Edge Config 用於租戶設定(網域對應、功能旗標、方案資訊)。
@vercel/sdk用於網域管理。DB 連線以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 中運作,表示平台正在洩漏。
- 在不破壞邊界的情況下擴充
- 新增佇列、工作流程或容器作為選用模式。保持路由明確且隔離完整。
注意事項
- 租戶標頭放在 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.txt 和 curl -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 和結構化資料。



