本文约 3,312 字,预计阅读 9 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:二十个站部署了二十套 WordPress,升级到吐血
做矩阵的读者几乎都经历过这个阶段:第一个站跑通,第二个站复制粘贴,到了第十个站,插件更新要登十次后台,SSL 证书要配十份,某个安全补丁爆出来要在夜里紧急挨个升级。于是自然想到 WordPress 自带的多站点网络(Multisite / Network)。但真切换过去会遇到另一面:所有子站共用同一套数据库,一个表的锁可能影响全站;某个子站被攻破,同库其他站也危险;想把其中一个站卖给客户,拆分极其麻烦。选错架构的代价,在半年后才开始显现。
原理分析:Multisite 到底共享了什么
开启 Multisite 后,WordPress 会新建若干全局表:wp_blogs、wp_site、wp_sitemeta、wp_blogmeta、wp_signups、wp_registration_log、wp_sitecategories(WP 5.1+ 起 wp_blogmeta 才存在)。用户与角色集中存在 wp_users / wp_usermeta,每个子站的内容表带站点前缀:wp_2_posts、wp_3_posts 等。网络管理员在「我的站点 → 网络管理」里统一管理插件与主题,各子站站长只能启用被允许的主题。
这意味着:共享 的是代码层(核心/插件/主题)、用户池、数据库实例;隔离 的是内容表、选项配置、媒体目录(wp-content/uploads/sites/{blog_id}/)。
对比另一条路线:独立安装 × N。它的隔离性最强,但运维成本近似线性增长;可以用 WP-CLI 脚本 + 统一的 Docker/CI 流程摊薄成本。
| 维度 | Multisite | 独立安装 |
|---|---|---|
| 升级维护 | 一次到位 | 逐站执行(可用脚本批量) |
| 故障隔离 | 弱(共库共进程) | 强 |
| 站点拆分/售卖 | 困难 | 简单(导出即可) |
| 数据库压力 | 集中,难按站拆分 | 可分散到不同实例 |
| 适用 | 学校院系、企业内部子站 | 流量独立、互不相关的内容站 |
保姆级实操
步骤 1:开启网络。 在 wp-config.php 的 /* 好了!请不要再继续编辑 */ 之前加一行,然后刷新后台会出现「网络设置」:
define( 'WP_ALLOW_MULTISITE', true );
注意:必须先停用全部插件,且站点若已有固定链接结构,子目录模式可能与现有 slug 冲突。
步骤 2:按系统提示写入配置。 网络设置完成后,WordPress 会给出两段需要手工粘贴的内容。子域名模式的典型写法是:
define( 'MULTISITE', true );
define( 'SUBDOMAIN_INSTALL', true );
define( 'DOMAIN_CURRENT_SITE', 'example.com' );
define( 'PATH_CURRENT_SITE', '/' );
define( 'SITE_ID_CURRENT_SITE', 1 );
define( 'BLOG_ID_CURRENT_SITE', 1 );
步骤 3:配置 Nginx 重写。 子域名模式相对简单,子目录模式需要匹配子目录规则:
server {
server_name example.com *.example.com;
root /var/www/html;
location / {
try_files $uri $uri/ /index.php?$args;
}
# 子目录型多站点必须拦截这两个组合,否则后台 JS 全部 404
rewrite ^/([_0-9a-zA-Z-]+/)?wp-admin$ /$1wp-admin/ permanent;
if (!-e $request_filename) {
rewrite ^/[_0-9a-zA-Z-]+/(wp-(content|admin|includes).*) /$1 last;
rewrite ^/[_0-9a-zA-Z-]+/(.*\.php)$ /$1 last;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
步骤 4:用程序统一管理站点。 get_sites() 返回站点列表,switch_to_blog() / restore_current_blog() 用于跨站操作:
$sites = get_sites( [ 'number' => 0, 'fields' => 'ids' ] );
foreach ( $sites as $blog_id ) {
switch_to_blog( $blog_id );
$count = wp_count_posts( 'post' )->publish;
update_option( 'mf_last_sync', current_time( 'mysql', 1 ) );
printf( "站点 %d:已发布文章 %d 篇\n", $blog_id, $count );
restore_current_blog();
}
务必成对调用 restore_current_blog(),漏掉会导致后续查询还在别人家的表里。
步骤 5:批量运维交给 WP-CLI。 多站点场景 WP-CLI 有专门的 --url 参数:
wp site list --fields=blog_id,url,public
wp plugin update --all --url=campus-a.example.com --allow-root
wp cron event run --due-now --url=campus-b.example.com
步骤 6:配置共享 Cookie 域。 子域名模式下要让所有子域共享登录 Cookie:
define( 'COOKIE_DOMAIN', '.example.com' );
步骤 7:备份策略。 Multisite 的 mysqldump 是整库备份,单站回滚几乎做不到——这也是它最大的运维风险点。
常见踩坑
get_sites()默认只返回 100 个站点,站群规模大了漏处理,必须显式写'number' => 0。- 案例: 某公司用 Multisite 搭了 40 个客户站,其中一个客户上传了带后门的插件并在网络层启用,导致整个网络所有站点被批量挂马,恢复耗时三天。
- 子目录模式下看了旧教程的 Apache
.htaccess规则,Nginx 环境直接照抄导致后台 404。 switch_to_blog()后忘记恢复,缓存插件在错误的站点上下文写入了对象缓存。- 泛域名证书没配齐,子站 HTTPS 全红。
COOKIE_DOMAIN配错导致登录互相踢下线。- 以为 Multisite 能分担负载,实际上数据库仍是单点。WordPress 站群风险与合规边界
总结
选择标准是:站点之间是否需要共享用户与统一升级(选 Multisite),以及是否需要风险隔离与独立售卖(选独立安装)。行动清单:明确站点关系 → 定子域名还是子目录 → 停用插件再开启 → Nginx 重写与泛域名证书 → WP-CLI 批量运维脚本 → 全库备份 + 单站导出双保险。对于互相无关的流量站,我更倾向独立安装 + Git/CI 管理,运维的复杂度可以摊薄,风险边界却清晰得多。多站点的备份与恢复策略另见 WordPress 数据库备份与恢复。
