WordPress Multisite 不是多站點架構:可擴展至 500 個位置的架構

WordPress Multisite 聽起來像是你會獲得多個站點。但實際上你得到的是一個 WordPress 安裝,它透過在數據庫表名稱前面添加一個數字來假裝是多個站點。你的主站點使用 wp_posts。子站點 2 使用 wp_2_posts。子站點 3 使用 wp_3_posts。這不是多站點架構。這是一個數據庫,其中包含相同表格的編號副本。而它有後果。

我已將具有 15、40 和一次 87 個子站點的網路從 WordPress Multisite 進行遷移。每一次,客戶都認為他們正在獲得獨立站點。每一次,他們都以艱難的方式發現他們並沒有。如果你在 WordPress Multisite 上運營加盟店、多地點業務或大學部門網路,這篇文章會有點令人不適。但現在了解要比子站點 #47 導致所有其他 46 個站點癱瘓之後才知道要好得多。

目錄

WordPress Multisite 不是多站點架構:可擴展至 500 個位置的架構

WordPress Multisite 在後台實際做的事

讓我們看看在 WordPress 中啟用 Multisite 時會發生什麼。你在 wp-config.php 中添加幾行程式碼:

define('WP_ALLOW_MULTISITE', true);
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', false);
define('DOMAIN_CURRENT_SITE', 'example.com');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);

WordPress 隨後會建立幾個網路級別的表格(wp_blogswp_sitewp_sitemetawp_registration_logwp_signups),並開始為每個新的子站點複製你的核心表格結構。

以下是在只有 5 個子站點後數據庫的樣子:

wp_posts              (主站點)
wp_postmeta           (主站點)
wp_options            (主站點)
wp_users              (共享 - 所有站點)
wp_usermeta           (共享 - 所有站點)
wp_2_posts            (子站點 2)
wp_2_postmeta         (子站點 2)
wp_2_options          (子站點 2)
wp_3_posts            (子站點 3)
wp_3_postmeta         (子站點 3)
wp_3_options          (子站點 3)
...
wp_5_posts            (子站點 5)
wp_5_postmeta         (子站點 5)
wp_5_options          (子站點 5)

五個子站點,你已經有大約 55 個表格。五十個子站點?你會看到一個 MySQL 數據庫中有超過 500 個表格。五百個位置?超過 5,000 個表格。在一個數據庫中。共享一個連接池。

每個表格都有自己的索引。每個查詢都針對一個特定的前綴表格。查詢規劃程式必須處理所有這些。而這些表格中的每一個都可由在該伺服器上運行的任何 PHP 進程訪問,因為它們都在具有相同憑據的同一數據庫中。

這不是多站點架構。這是一個偽裝成隔離的命名約定。

假多站點架構的 7 個後果

1. 共享漏洞表面

WordPress Multisite 網路中的每個子站點都運行相同的 WordPress 核心、相同的外掛和相同的主題。一個外掛漏洞會影響所有子站點,因為它們共享相同的程式碼庫。

這不是理論上的。在 2026 年 2 月,WPVivid —— 一個擁有超過 900,000 個活躍安裝的備份外掛 —— 披露了一個嚴重程度為 9.8 的 RCE(遠端程式碼執行)漏洞。嚴重程度滿分 10 中的 9.8。這是「未經驗證的攻擊者可以在你的伺服器上執行任意程式碼」的級別。

在獨立的 WordPress 站點上,這是一個被入侵的站點。在具有 30 個子站點的 Multisite 網路上?那是 30 個被入侵的站點。相同的伺服器。相同的數據庫。相同的檔案系統。一個漏洞,整個網路被入侵。

你不能在子站點 #12 上安裝不同版本的外掛,而在子站點 #13 上卻不能。你無法將一個位置的外掛沙箱化與另一個位置分離。每個站點都會獲得相同的攻擊表面。

2. 外掛衝突倍增

在單個 WordPress 站點上,外掛衝突會破壞一個站點。你停用外掛,診斷,然後繼續。令人煩惱但可控。

在 Multisite 上,網路啟用的外掛衝突會同時破壞每個站點。我見過一個 WooCommerce 更新在 Multisite 網路上導致 23 個位置頁面癱瘓,因為一個網路啟用的快取外掛還沒有針對新的 WooCommerce 掛鉤進行更新。23 個位置有破損頁面。一個根本原因,23 位憤怒的位置管理員打電話給同一個人。

而這是關鍵的地方 —— 個別站點管理員通常無法停用網路啟用的外掛。他們必須等待超級管理員修復它。

3. 性能退化

五十個子站點共享一個 MySQL 實例。子站點 #47 上的每個頁面加載都運行以下查詢:

SELECT * FROM wp_47_posts WHERE post_type = 'page' AND post_status = 'publish';
SELECT option_value FROM wp_47_options WHERE option_name = 'active_plugins';
SELECT * FROM wp_47_postmeta WHERE post_id IN (142, 143, 144, 145);

同時,子站點 #12 正在針對 wp_12_postswp_12_optionswp_12_postmeta 運行自己的一組查詢。每個其他子站點也是如此,所有這些都擊中相同的 MySQL 實例。

MySQL 的查詢規劃程式感到困惑。表格快取填滿。每個前綴表格都維護自己的索引,但緩衝池是共享的。隨著你添加更多子站點,性能會非線性退化。從 10 個子站點增加到 20 個不是兩倍的負載 —— 根據流量模式,由於鎖定爭用和緩衝池抖動,可能是三倍或四倍的負載。

我曾經基準化過一個 40 子站點網路。子站點 #1 上的平均查詢時間是 45 毫秒。當我們測試子站點 #38 時,平均查詢時間已經爬升至 380 毫秒。相同的查詢結構。每個站點相同的數據量。數據庫只是在被表格淹沒。

4. 遷移是一場噩夢

嘗試將子站點 #23 從 50 站點網路提取到它自己的獨立 WordPress 安裝中。以下是你需要做的事情:

  1. 導出所有 wp_23_ 前綴的表格
  2. 將每個表格從 wp_23_ 前綴重新映射到 wp_ 前綴
  3. 重新序列化所有選項和小工具數據(WordPress 將序列化的 PHP 儲存在數據庫中,當你更改前綴時字符串長度會改變)
  4. 將媒體路徑從 /uploads/sites/23/ 重新映射到 /uploads/
  5. 搜索並替換所有內部 URL
  6. wp_usermeta 中將用戶功能從 wp_23_capabilities 重新映射到 wp_capabilities
  7. 從共享的 wp_users 表格中提取用戶(只有那些屬於子站點 #23 的用戶)
  8. 重新建立用戶與站點的關係

序列化中出現一個錯誤,你就會得到被破壞的數據。小工具設置消失。主題自訂程式選項斷裂。菜單結構消失。我已經執行過數十次這個提取過程,即使使用 WP Migrate DB Pro 之類的工具,每個子站點也需要花費 4-8 小時。如果你正在停用網路,將其乘以 50 個子站點。

WordPress 生態系統對此有工具,當然。但是這些工具需要存在的事實告訴你關於架構的一些事情。

5. 沒有真正的數據隔離

這是那個應該會讓你害怕的一個,如果你關心安全性或合規性的話。

子站點 #2 上的 SQL 注入漏洞不只會暴露 wp_2_posts。連接到 MySQL 的數據庫用戶可以訪問數據庫中的每個表格。這意味著 wp_posts(主站點)、wp_3_posts(子站點 3)、wp_users(所有站點中的所有用戶)和其他所有表格。

沒有數據庫級別的隔離。沒有行級安全性。沒有模式分離。WordPress Multisite 是一個數據庫、一組憑據和一個命名約定。就是這樣。

對於在多個位置處理客戶數據的企業 —— 醫療辦公室、律師事務所、金融服務 —— 這是一個合規問題。HIPAA、SOC 2 和 PCI DSS 都對數據隔離有要求。「我們使用了不同的表格前綴」不會滿足審計員。

6. 超級管理員瓶頸

WordPress Multisite 有一個稱為超級管理員的角色。只有超級管理員可以:

  • 安裝或刪除外掛
  • 安裝或刪除主題
  • 啟用網路範圍的外掛
  • 向網路添加新站點
  • 管理網路設置

個別站點管理員的能力受限。他們無法安裝自己的外掛。他們無法添加自己的主題。每個涉及共享基礎架構的更改都要經過一個人(或一個小團隊)。

對於 3 站點網路,這沒問題。對於 50 位置的加盟品牌,其中每個位置經理想要添加自己的預訂小工具或菜單 PDF 外掛?它是一個引起不滿和影子 IT 的瓶頸。

7. 域名映射脆弱性

想要每個位置有自己的域名?denver.yourbrand.comyourbrand-denver.com?WordPress Multisite 不能以可靠的方式本地處理此問題。你需要域名映射解決方案,而內置的 sunrise.php dropin 方法以出了名地脆弱而聞名。

映射域名的 SSL 證書添加了另一層。DNS 配置添加了另一層。每個映射的域名都是另一個潛在故障點,你的超級管理員必須管理。一個 DNS 更改、一個過期的證書、一個錯誤配置的映射條目,一個位置的站點就會下線。

WordPress Multisite 何時有效(老實說)

我不打算假裝它毫無用處。WordPress Multisite 在特定場景中表現很好:

  • 小型網路(低於 10 個站點),其中所有站點由同一團隊管理
  • 大學部門站點,其中集中控制實際上是理想的
  • 開發/測試/生產鏡像,用於同一項目
  • 部落格網路,其中內容隔離不是關鍵

如果你有 5-8 個站點、一個稱職的系統管理員,並且你不需要站點之間的數據隔離 —— Multisite 很好。當你嘗試將其擴展到數十或數百個位置時,問題開始出現。

WordPress Multisite 不是多站點架構:可擴展至 500 個位置的架構 - 架構

實際上可擴展至 500 個位置的架構

以下是我們在 Social Animal 為多位置企業使用的替代方法:一個無頭架構,前端使用 Next.js,後端使用 Supabase(PostgreSQL),使用行級安全性(RLS)實現真正的數據隔離。

核心想法:與其為每個位置複製表格,你擁有一組表格,帶有一個 location_id 列。數據庫級別的安全策略確保查詢只返回授權位置的數據。與其有 500 個獨立的 WordPress 安裝假裝是獨立的,你有一個應用程式部署,其中 /locations/[slug] 從數據庫行動態呈現每個位置的頁面。

行級安全性實際上如何運作

-- 建立位置表
CREATE TABLE locations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  slug TEXT UNIQUE NOT NULL,
  name TEXT NOT NULL,
  address TEXT,
  phone TEXT,
  hours JSONB,
  metadata JSONB,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- 建立具有位置隔離的頁面表
CREATE TABLE pages (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  location_id UUID REFERENCES locations(id),
  title TEXT NOT NULL,
  content JSONB,
  slug TEXT NOT NULL,
  published BOOLEAN DEFAULT false,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- 啟用 RLS
ALTER TABLE pages ENABLE ROW LEVEL SECURITY;

-- 策略:位置管理員只能看到自己位置的頁面
CREATE POLICY "location_isolation" ON pages
  FOR ALL
  USING (location_id = (SELECT location_id FROM user_locations WHERE user_id = auth.uid()));

-- 策略:公眾可以閱讀任何位置的已發佈頁面
CREATE POLICY "public_read" ON pages
  FOR SELECT
  USING (published = true);

有了這個設置,丹佛的位置經理,即使不知何故製作了一個惡意查詢,從物理上也不能訪問奧斯汀的數據。數據庫強制執行它。不是應用程式層。不是命名約定。數據庫本身。

架構比較:前綴表與行級安全性

以下是兩種架構的視覺表示:

WordPress Multisite 架構:

┌─────────────────────────────────────────────┐
│           單個 MySQL 數據庫                   │
│                                             │
│  wp_posts          (主站點)                  │
│  wp_options        (主站點)                  │
│  wp_2_posts        (丹佛)                     │
│  wp_2_options      (丹佛)                     │
│  wp_3_posts        (奧斯汀)                   │
│  wp_3_options      (奧斯汀)                   │
│  wp_4_posts        (西雅圖)                   │
│  wp_4_options      (西雅圖)                   │
│  ... x 500 位置 = 5,000+ 表格                │
│                                             │
│  ⚠️  一組數據庫憑據                          │
│  ⚠️  一個 PHP 進程訪問所有表格              │
│  ⚠️  零數據庫級別隔離                        │
└─────────────────────────────────────────────┘

Next.js + Supabase 架構:

┌─────────────────────────────────────────────┐
│         單個 PostgreSQL 數據庫                │
│                                             │
│  locations    (500 行,每個位置一個)        │
│  pages        (每個位置的內容)              │
│  media        (每個位置的圖像)              │
│  staff        (每個位置的團隊)              │
│  reviews      (每個位置的評論)              │
│                                             │
│  ✅  RLS 策略強制隔離                        │
│  ✅  丹佛用戶無法查詢奧斯汀數據             │
│  ✅  5 個表格總計,不是 5,000               │
│  ✅  標準索引,最佳查詢計劃                  │
└─────────────────────────────────────────────┘

兩種情況下都是一個數據庫。但隔離模型根本上不同。RLS 在 PostgreSQL 引擎級別強制執行 —— 不是應用程式可以繞過的東西。

因素 WordPress Multisite Next.js + Supabase
500 位置時的表格 ~5,500+ ~5-15
數據隔離 無(僅命名約定) 數據庫強制 RLS
漏洞表面 一個漏洞 = 所有站點 按位置身份驗證隔離
外掛衝突 網路範圍內停機 不適用(無外掛架構)
添加位置 建立子站點 + 配置 插入數據庫行
移除位置 複雜提取過程 刪除帶有 CASCADE 的行
域名映射 需要外掛,脆弱 中間件重寫,原生
構建/部署時間 不適用(運行時 PHP) ~30 秒增量構建
TTFB(平均,無快取) 800 毫秒 - 2 秒+ 50-150 毫秒(邊緣)
每月託管(500 個站點) $200-800/月(託管 WP) $25-75/月(Supabase + Vercel)
從入侵恢復 完整網路修復 輪換金鑰,重新部署

實施:用於多位置站點的 Next.js + Supabase

以下是使用 Next.js 在實踐中路由的工作方式:

// app/locations/[slug]/page.tsx
import { createClient } from '@/lib/supabase/server';
import { notFound } from 'next/navigation';

export async function generateStaticParams() {
  const supabase = createClient();
  const { data: locations } = await supabase
    .from('locations')
    .select('slug');
  
  return locations?.map(({ slug }) => ({ slug })) ?? [];
}

export default async function LocationPage({ 
  params 
}: { 
  params: { slug: string } 
}) {
  const supabase = createClient();
  
  const { data: location } = await supabase
    .from('locations')
    .select(`
      *,
      pages(*),
      staff(*),
      reviews(*, rating)
    `)
    .eq('slug', params.slug)
    .single();
  
  if (!location) notFound();
  
  return (
    <div>
      <h1>{location.name}</h1>
      <LocationHero location={location} />
      <LocationServices pages={location.pages} />
      <LocationTeam staff={location.staff} />
      <LocationReviews reviews={location.reviews} />
    </div>
  );
}

添加新位置?插入一行:

INSERT INTO locations (slug, name, address, phone, hours)
VALUES (
  'portland-or',
  'Portland, OR',
  '123 Burnside St, Portland, OR 97209',
  '(503) 555-0142',
  '{"mon": "9-5", "tue": "9-5", "wed": "9-5"}'
);

就是這樣。下一個構建會選擇它。或者如果你使用 ISR(增量靜態再生),它會在你的再驗證窗口內顯示,無需構建。

移除位置?DELETE FROM locations WHERE slug = 'portland-or'; 級聯刪除會處理其餘的。沒有 4-8 小時的提取過程。

對於每個位置的自訂域名,Next.js 中間件乾淨地處理它:

// middleware.ts
import { NextResponse } from 'next/server';

const DOMAIN_MAP: Record<string, string> = {
  'yourbrand-denver.com': '/locations/denver-co',
  'yourbrand-austin.com': '/locations/austin-tx',
  // ... 在生產中從邊緣配置加載
};

export function middleware(request: Request) {
  const hostname = new URL(request.url).hostname;
  const rewritePath = DOMAIN_MAP[hostname];
  
  if (rewritePath) {
    return NextResponse.rewrite(new URL(rewritePath, request.url));
  }
  
  return NextResponse.next();
}

沒有外掛。沒有 sunrise.php dropin。沒有脆弱的映射表。只是邊緣的重寫規則。

遷移路徑:擺脫 WordPress Multisite

如果你目前在 WordPress Multisite 上有 20 個以上的位置,以下是現實的遷移路徑:

第 1 階段:數據導出(1-2 週)

使用 WordPress REST API 或 WP-CLI 從每個子站點導出內容。不要嘗試手動重新映射前綴表格。使用 API —— 它抽象了前綴的噩夢。

# 從子站點 23 導出所有帖子
wp post list --url=example.com/location-23 --format=json > location-23-posts.json

# 導出所有媒體
wp media list --url=example.com/location-23 --format=json > location-23-media.json

第 2 階段:模式設計(1 週)

圍繞你的實際內容模型而不是 WordPress 的帖子/postmeta 模型設計你的 Supabase 模式。這是修復多年積累的數據模型技術債務的時刻。

第 3 階段:內容遷移(1-2 週)

編寫遷移腳本,將 WordPress 內容轉換為你的新模式。處理序列化數據、短代碼和古騰堡塊。

第 4 階段:前端構建(3-4 週)

構建 Next.js 前端。這是你看到實際性能收益的地方。在 WordPress 上耗時 1.5 秒的頁面現在在 200 毫秒以下加載。

第 5 階段:DNS 切換(1 天)

將你的域名指向新基礎架構。保持舊網路作為 30 天的唯讀備份運行。

對於需要此遷移過程幫助的企業,我們已在我們的 無頭 CMS 開發頁面上記錄了我們的方法。我們已經做了足夠多的遷移來知道屍體埋在哪裡。

真實數字:性能和成本比較

以下是我們在 2025 年第 1 季度完成的實際遷移數據 —— 一個擁有 34 個位置的牙科診所網路:

指標 WordPress Multisite(之前) Next.js + Supabase(之後)
平均 TTFB 1,240 毫秒 89 毫秒
最大內容繪製 3.8 秒 1.1 秒
每月託管成本 $347/月(WP Engine) $45/月(Vercel Pro + Supabase Pro)
添加新位置的時間 2-3 小時(手動設置) 15 分鐘(插入行 + 內容)
更新所有位置的時間 外掛更新 + 祈禱 git push
核心網路指標通過率 34 個位置中的 12 個 34 個位置中的 34 個
安全事件(12 個月) 3 0

僅託管成本節省就在 8 個月內支付了遷移費用。性能改進在 90 天內為 28 個位置中的 34 個推動了可測量的本地搜索排名增加。

如果你正在為自己的網路評估成本,我們的 定價頁面對多位置項目有透明的細分。

常見問題

WordPress Multisite 適合管理多個位置嗎? 對於少量位置(低於 10 個)有集中管理,WordPress Multisite 可以工作。但它的設計初衷不是為了多位置企業需要獨立運營、數據隔離或大規模高性能。共享數據庫架構建立了安全、性能和運營問題,這些問題隨著你添加的每個位置而增加。

WordPress Multisite 的最大問題是什麼? 七個主要問題是:共享漏洞表面(一個漏洞擊中所有站點)、外掛衝突倍增(一個衝突導致每個站點停機)、非線性性能退化、噩夢般的遷移/提取過程、零數據庫級別的數據隔離、超級管理員瓶頸用於所有管理更改,以及脆弱的域名映射。這些問題是架構性的 —— 它們無法通過外掛或更好的託管來修復。

WordPress Multisite 可以處理 100 多個站點嗎? 從技術上講,是的。從實踐上講,超過 30-50 個站點會變得非常痛苦。在 100 個站點,你處理單個 MySQL 實例中 1,100 多個數據庫表,嚴重的查詢規劃程式退化,以及需要專門 DevOps 人員的操作複雜性。在 500 個站點,對於大多數託管環境來說,這是不可行的。

WordPress Multisite 的最佳替代方案是什麼,用於多個位置? 使用無頭架構,前端使用 Next.js 或 Astro,後端使用 PostgreSQL 數據庫(例如 Supabase),使用行級安全性提供真正的數據隔離,戲劇性地改進性能,降低託管成本,以及微不足道的位置管理。每個位置是一個數據庫行,而不是整個 WordPress 安裝。

你如何從 WordPress Multisite 遷移到無頭架構? 最安全的方法是通過 WordPress REST API 或 WP-CLI 而不是嘗試手動重新映射前綴表格來提取內容。按子站點導出內容,在目標數據庫中設計乾淨的模式,編寫轉換腳本,構建新前端,並切換 DNS。對於 20 個以上站點的網路,計劃總共需要 6-10 週。

WordPress Multisite 影響 SEO 嗎? 間接地,是的。WordPress Multisite 的性能退化導致頁面加載速度變慢,這會傷害核心網路指標得分。Google 已確認頁面體驗信號影響排名。在我們的遷移數據中,在遷移到具有低於 200 毫秒 TTFB 的無頭架構後 90 天內,82% 的位置看到了改進的本地搜索排名。

WordPress Multisite 對業務數據安全嗎? 否,如果你將安全定義為包括位置之間的數據隔離。WordPress Multisite 使用一個數據庫和一組憑據。任何子站點上的 SQL 注入都可以訪問每個其他子站點的數據。對於遵守 HIPAA、SOC 2、PCI DSS 或類似合規要求的企業,缺乏數據庫級別隔離是一個重大風險。

運行 WordPress Multisite 與無頭替代方案相比花費多少? Multisite 的託管 WordPress 託管通常運行 $200-800/月,取決於站點數量和流量級別(WP Engine 的 Multisite 計劃在 2025 年從其增長層 $240/月開始)。可比的無頭設置在 Vercel Pro ($20/月) 加上 Supabase Pro ($25/月) 上可以以成本的一小部分處理相同的流量。無頭方法的初始構建投資較高,但每月運營成本顯著降低。如果你想要針對你的網路規模的具體成本比較,請隨時 聯繫我們