Multisite Networks

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This sub-section’s WordPress bibliography lists the reference material consulted while preparing these pages.

A Multisite network is one WordPress installation that serves many sites. The sites share one copy of core, one set of plugin and theme files, one user table and one database, but each has its own content, settings, theme choice and administrators. A super admin runs the whole network from Network Admin. Multisite has been part of core since WordPress 3.0 (2010), when the separate WordPress MU project was merged in.

This page was written against WordPress 7.1.3 (released 2026-10-06; 7.1.4, a security release of 2026-10-28, is the version to run in production), PHP 7.4 minimum and 8.3+ recommended, MariaDB 11.8, Apache 2.4 and nginx 1.29, and WP-CLI 2.12.0, and checked on 2026-10-28. Every command and every result below was run on a fresh 7.1.3 network in containers: once as a subdirectory network (converted from a single site, behind both Apache and nginx) and once as a subdomain network (installed directly). WordPress 7.2 (in beta, due in December 2026) announces no Multisite changes so far.

The running example is the publisher Lumen Press, which adds two imprints, Lumen Kids and Lumen Noir, as sites of one network. Local examples use lumenpress.test; production names use lumenpress.example and, for the mapped imprint, lumennoir.example.

When a network is the right tool

A network fits sites that are run by one organization, share code, and are administered centrally, while their content stays separate. It does not make sites share content: a post on Lumen Kids is not visible to Lumen Noir unless code copies or queries it across sites.

Good fit Poor fit

Imprints, brands, departments, campuses or franchise locations with the same plugins and a shared design system

Sites that need different PHP versions, different plugin versions, or separate release schedules

Many similar sites created on demand (a blog host, a course platform, a site per event)

Sites owned by different clients who must not be able to affect each other

One login for staff who work on several sites

One site that only needs a second language or a second section (use a multilingual plugin, a custom post type or a page tree instead)

Central control of updates, security and which themes are allowed

Sites that must be moved, sold or hosted elsewhere independently; splitting a site out of a network is manual work

The shared code is both the benefit and the risk. One plugin update reaches every site at once, a fatal error in a network-activated plugin takes down every site, and every site shares one PHP process pool and one database server. Many plugins are written and tested for single sites only; check each one for Multisite support before relying on it. Lumen Press accepts these trade-offs because both imprints use the same lumen-catalog plugin and the lumen theme family, and the same editors work on all three sites.

How a network is put together

Topology of the Lumen Press network: three addresses reach one installation, which picks the site from wp_blogs; three sites with their own tables share code and global tables

Every request goes through wp-includes/ms-settings.php, which reads the host name and path, looks them up in the wp_blogs table, and sets the current network and site before plugins load. From then on $wpdb->prefix points at that site’s tables (wp_ for the main site, wp_2_ for site 2) while users and network data stay in shared tables.

The vocabulary in the database is historical and inverted. In code and tables a site (wp_site, get_current_network_id()) is the network and a blog (wp_blogs, blog_id) is a site; the admin screens use network and site. The main site is the site with BLOG_ID_CURRENT_SITE, normally blog ID 1, which keeps the base table prefix and the base uploads folder.

Subdomains or subdirectories

Each network chooses one addressing scheme when it is created. Individual sites can still be moved to other domains later through domain mapping, described below.

Subdirectories (path-based) Subdomains (domain-based)

Site address

lumenpress.example/kids/

kids.lumenpress.example

SUBDOMAIN_INSTALL

false

true

DNS

Nothing beyond the main record

A record per site, or a wildcard record for on-demand sites

TLS

The main certificate covers every site

A wildcard certificate, or one certificate per name

Main site permalinks

Posts get a forced /blog/ prefix (/blog/%postname%/) so they cannot clash with site slugs; pages do not

No prefix

Reserved slugs

page, comments, blog, files, feed, wp-admin, wp-content, wp-includes, wp-json, embed cannot be site names

Banned names only (www, admin, …​)

Login cookies

Host-only cookies on the main domain

COOKIE_DOMAIN defaults to .lumenpress.example, so one login covers every subdomain

Not allowed when

The site has posts published more than a month ago (their URLs would clash with site paths), unless ALLOW_SUBDIRECTORY_INSTALL is true

The site address is localhost, an IP address, or contains a path

Neither scheme works when the WordPress address uses a port other than 80 or 443. For local work, use a name such as lumenpress.test mapped to 127.0.0.1 in the hosts file rather than localhost:8080.

The one-month rule comes from allow_subdirectory_install(), which looks for a published post older than a month in wp_posts; the allow_subdirectory_install filter and the ALLOW_SUBDIRECTORY_INSTALL constant override it. The conversion also replaces the main site’s permalink structure: a site converted with /%postname%/ came out with /blog/%year%/%monthnum%/%day%/%postname%/, and setting /%postname%/ again stored /blog/%postname%/, so /hello-world/ answered 404 and /blog/hello-world/ 200. Plan redirects for existing post URLs before converting a site that already has traffic.

Switching an existing network between the two schemes later means changing SUBDOMAIN_INSTALL, every row of wp_blogs, the home and siteurl options of each site, URLs in content, the rewrite rules, DNS and certificates. It is possible but rarely worth it; choose carefully at the start. Lumen Press uses subdirectories for the imprints (one certificate, one DNS record) and maps Lumen Noir to its own domain.

Enabling Multisite

Preparing the site

Before converting an existing site:

  1. Take a full backup of the database and files (see Backups and Disaster Recovery).

  2. Make sure pretty permalinks work, because Multisite routing depends on rewrites.

  3. Deactivate all plugins; reactivate them one by one afterwards and decide per plugin whether to network-activate it.

  4. If WordPress should live in its own directory, move it before enabling Multisite (supported since 3.5, but not afterwards).

  5. Settle the final domain and whether it uses www; changing the main domain later touches every site.

From the dashboard: Network Setup

Multisite is switched on in two steps. First, WP_ALLOW_MULTISITE makes the Tools → Network Setup screen (wp-admin/network.php) appear. Add it to wp-config.php above the /* That's all, stop editing! */ line:

/* Multisite: shows Tools > Network Setup. */
define( 'WP_ALLOW_MULTISITE', true );

On 7.1.3 the screen, titled Create a Network of WordPress Sites, asks for the addressing scheme (it states You cannot change this later. and that subdomains need a wildcard DNS record), the Network Title and the Network Admin Email, and shows the Server Address it will use. An option that is not allowed for the site (see the table above) is not offered. Install creates the network tables and then shows the lines to add to wp-config.php and the rewrite rules for .htaccess (or the advice to configure nginx). After saving both files, log in again: the login cookies change.

With WP-CLI

wp core multisite-convert turns an installed single site into a network; wp core multisite-install installs a network on an empty database in one step. Both write the constants to wp-config.php unless --skip-config is given, and neither writes rewrite rules.

# An existing single site (Lumen Press) becomes a subdirectory network
wp core multisite-convert --title="Lumen Press Network"
Set up multisite database tables.
Added multisite constants to 'wp-config.php'.
Success: Network installed. Don't forget to set up rewrite rules (and a .htaccess file, if using Apache).

If WP_ALLOW_MULTISITE was already defined for the dashboard route, the conversion adds a second define() and every later request logs PHP Warning: Constant WP_ALLOW_MULTISITE already defined. Delete one of the two lines.

A fresh subdomain network, on an empty database, in one command:

wp core multisite-install --subdomains --url=https://lumenpress.test \
  --title="Lumen Press Network" --admin_user=admin \
  --admin_email=admin@lumenpress.test --admin_password="$WP_ADMIN_PASSWORD" --skip-email

It printed Created single site database tables., then Warning: Wildcard DNS may not be configured correctly., then the same two closing lines as the conversion. The warning comes from the installer’s test request to a random subdomain. It is harmless when sites are created by hand with their own DNS records, or before the wildcard record exists; a working wildcard record makes it disappear. --subdomains is refused for localhost. Without --subdomains the same command creates a subdirectory network.

The wp-config.php constants

Both routes produce this block (shown for a subdirectory network on the production host):

define( 'WP_ALLOW_MULTISITE', true );
define( 'MULTISITE', true );
define( 'SUBDOMAIN_INSTALL', false );
$base = '/';
define( 'DOMAIN_CURRENT_SITE', 'lumenpress.example' );
define( 'PATH_CURRENT_SITE', '/' );
define( 'SITE_ID_CURRENT_SITE', 1 );
define( 'BLOG_ID_CURRENT_SITE', 1 );

/* That's all, stop editing! Happy publishing. */
Constant Meaning

WP_ALLOW_MULTISITE

Only shows Tools → Network Setup. It can stay or go once the network exists.

MULTISITE

Turns the network on. Removing it makes WordPress behave as the main site only.

SUBDOMAIN_INSTALL

true for subdomains, false for subdirectories.

DOMAIN_CURRENT_SITE, PATH_CURRENT_SITE

The network’s main domain and path. Requests whose host matches no row in wp_blogs fall back to this network.

SITE_ID_CURRENT_SITE

The network ID (wp_site.id), 1 unless several networks share the tables.

BLOG_ID_CURRENT_SITE

The main site’s blog_id, normally 1.

$base

A legacy variable from WordPress MU, still written by the installer; the debugging guide checks it.

Optional constants that matter on a network:

Constant Effect

NOBLOGREDIRECT

Subdomain networks: where a request for a non-existent site goes. Without it, a visit to nosuch.lumenpress.test was redirected to wp-signup.php?new=nosuch. Also used by maybe_redirect_404() for 404s on the main site, which surprises administrators who set it.

COOKIE_DOMAIN

Cookie scope. Needed for mapped domains on a subdomain network (see domain mapping).

WP_DEFAULT_THEME

The theme new sites start with (twentytwentyfive in 7.1.3). The default theme is network-enabled when the network is created.

ALLOW_SUBDIRECTORY_INSTALL

Offers subdirectories even for a site with posts older than a month.

SUNRISE

Loads the wp-content/sunrise.php drop-in early in ms-settings.php, before the site lookup. Custom routing plugins use it; native domain mapping does not need it.

WP_MEMORY_LIMIT

Defaults to 64M on Multisite (40M on a single site); WP_MAX_MEMORY_LIMIT stays 256M.

The full list of constants is on wp-config.php Reference.

Rewrite rules for Apache

Apache needs mod_rewrite, AllowOverride All for the document root, and FollowSymLinks or SymLinksIfOwnerMatch; without the last one Apache forbids RewriteRule, and the network shows "One or more database tables are unavailable". Replace the single-site # BEGIN WordPress block of .htaccess with the network rules. For a subdirectory network:

# BEGIN WordPress Multisite
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]
# END WordPress Multisite

The optional (/)?` group strips the site slug, so `/kids/wp-login.php` and `/kids/wp-includes/…​+ reach the shared files. A subdomain network uses the same block without that group; only these three rules differ:

RewriteRule ^wp-admin$ wp-admin/ [R=301,L]
RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
RewriteRule ^(.*\.php)$ $1 [L]

The virtual host must answer every name of the network, written in Apache 2.4 syntax:

<VirtualHost *:443>
    ServerName lumenpress.example
    ServerAlias *.lumenpress.example lumennoir.example
    DocumentRoot /var/www/lumenpress

    <Directory /var/www/lumenpress>
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    SSLEngine on
    SSLCertificateFile    /etc/ssl/lumenpress/fullchain.pem
    SSLCertificateKeyFile /etc/ssl/lumenpress/privkey.pem
</VirtualHost>

Rewrite rules for nginx

nginx reads no .htaccess file and needs no mod_rewrite equivalent beyond its own rewrite directive. A subdirectory network adds three rewrites for requests that do not match a file:

# /etc/nginx/conf.d/lumenpress.conf (subdirectory network)
upstream php {
    server 127.0.0.1:9000;    # or unix:/run/php/php8.4-fpm.sock
}

server {
    listen 80;
    server_name lumenpress.example lumennoir.example;
    root /var/www/lumenpress;
    index index.php;

    if (!-e $request_filename) {
        rewrite /wp-admin$ $scheme://$host$request_uri/ permanent;
        rewrite ^(/[^/]+)?(/wp-.*) $2 last;
        rewrite ^(/[^/]+)?(/.*\.php) $2 last;
    }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass php;
    }

}

A subdomain network uses the same file without the if block, and lists the wildcard in server_name:

server_name lumenpress.example *.lumenpress.example lumennoir.example;

Both variants pass nginx -t on nginx 1.29. Behind them, with PHP-FPM 8.4, the test network answered as follows (TLS termination is left out to keep the files short; the Web Servers: Apache and nginx page adds it):

Request Status Result

/kids/ (subdirectory)

200

Lumen Kids home page

/kids/wp-admin

301

Redirect to /kids/wp-admin/

/kids/wp-login.php

200

The shared wp-login.php, titled for Lumen Kids

kids.lumenpress.test/ (subdomain)

200

Lumen Kids home page

nosuch.lumenpress.test/ (subdomain)

302

wp-signup.php?new=nosuch on the main site

The blogs.dir and ms-files.php rules found in older guides apply only to networks created before WordPress 3.5. Newer networks store uploads in wp-content/uploads/sites/<blog_id>/ and serve them as plain static files.

Creating the Lumen Kids and Lumen Noir sites

Sites are created on Network Admin → Sites → Add Site (address, title, language, admin email) or with WP-CLI. The --slug becomes the path on a subdirectory network and the subdomain on a subdomain network:

wp site create --slug=kids --title="Lumen Kids" --email=admin@lumenpress.test
wp site create --slug=noir --title="Lumen Noir" --email=admin@lumenpress.test
wp site list --fields=blog_id,url

The commands reported Site 2 created: https://lumenpress.test/kids/ and Site 3 created: https://lumenpress.test/noir/, and wp site list showed blog IDs 1, 2 and 3.

Every WP-CLI command that should act on one site takes --url, for example wp option get blogname --url=lumenpress.test/kids/. Without it, commands act on the main site. A reserved slug is refused with Error: The following words are reserved and cannot be used as blog names: page, comments, blog, files, feed, wp-admin, wp-content, wp-includes, wp-json, embed. The dashboard also enforces lowercase letters and digits for site names; WP-CLI accepted --slug=Noir_2, so scripts should validate slugs themselves.

A new site starts with the default theme, a "Hello world!" post, a sample page and the content defined under New Site Settings. Its uploads go to wp-content/uploads/sites/2/ (wp_upload_dir() returned that basedir for Lumen Kids).

The tables a network adds

The conversion added six network tables (wp_blogs, wp_blogmeta, wp_site, wp_sitemeta, wp_signups, wp_registration_log) and each new site added ten tables, from wp_2_commentmeta to wp_2_terms. The users tables stay global. With the two imprints, wp db tables --network listed 38 tables: 6 network tables, the 12 tables of the main site and 2 × 10 site tables.

The meaning of each table, the ER diagram and the arithmetic for large networks are on Database Schema. The network debugging guide still lists a wp_blog_versions table; WordPress 7.1.3 does not create it.

Network Admin

Super admins reach Network Admin from My Sites in the toolbar (/wp-admin/network/). On 7.1.3 its menu has Dashboard (with Updates for the whole network and Upgrade Network, which runs database upgrades on every site like wp core update-db --network), Sites (All Sites, Add Site), Users, Themes and Plugins (each with its installer and file editor), and Settings (Network Settings, and Network Setup, which shows the configuration lines again).

Sites → Edit has four tabs: Info (the Site Address (URL), registration dates and the Public, Archived, Spam, Deleted and Mature flags), Users (add or remove users and set their role on that site), Themes (enable extra themes for that site only) and Settings (every option of the site, as a raw form). Archiving a site keeps it but answers visitors with HTTP 410 and This site has been archived or suspended.; WP-CLI has matching commands (wp site archive 3, wp site unarchive 3, wp site spam, wp site deactivate, wp site delete).

Settings → Network Settings holds network options stored in wp_sitemeta, readable with wp site option get:

Section Fields Option names

Operational Settings

Network Title, Network Admin Email

site_name, admin_email

Registration Settings

Allow new registrations, Registration notification, Add New Users, Banned Names, Limited Email Registrations, Banned Email Domains

registration, registrationnotification, add_new_users, illegal_names, limited_email_domains, banned_email_domains

New Site Settings

Welcome Email, Welcome User Email, First Post, First Page, First Comment and its author, email and URL

welcome_email, welcome_user_email, first_post, first_page, first_comment, …​

Upload Settings

Site upload space, Upload file types, Max upload file size

upload_space_check_disabled, blog_upload_space (100 MB), upload_filetypes, fileupload_maxk (1500 KB)

Language Settings

Default Language

WPLANG

Menu Settings

Enable administration menus: Plugins

menu_items

On the test network upload_space_check_disabled was 1, so the 100 MB quota per site is not enforced until the box is ticked. fileupload_maxk limits single files on top of PHP’s upload_max_filesize.

Super admins

A super admin has every capability on every site, including the network-only ones (manage_network, manage_sites, manage_network_users, manage_network_plugins, manage_network_themes, manage_network_options). The list of super admin logins is the site_admins network option, not a role:

wp super-admin list
wp super-admin add netops
wp super-admin remove netops
wp network meta get 1 site_admins --format=json

On the test network the list held only admin, stored as ["admin"] in wp_sitemeta.

A site Administrator on a network is a smaller role than on a single site. Checked with user_can() for a user who is Administrator of Lumen Kids only:

Capability Site administrator Super admin

unfiltered_html (post raw HTML and scripts)

no

yes

install_plugins, install_themes

no

yes

activate_plugins

no (yes when Menu Settings → Plugins is enabled)

yes

create_users

no (yes when Add New Users is enabled)

yes

edit_users, delete_users

no

yes

promote_users (add existing users to the site)

yes

yes

switch_themes, edit_theme_options, upload_files

yes

yes

manage_network

no

yes

Because a super admin can run code on every site, keep the list short, require two-factor authentication for those accounts (see Security Hardening), and give day-to-day editors per-site roles instead. Capability checks in plugin code are covered in Multisite Development.

Plugins and themes on a network

Only super admins install, update and delete plugins and themes; the files are shared by every site. There are three ways for plugin code to run:

Mode Behaviour

Network-activated

Runs on every site, stored in the active_sitewide_plugins network option; site admins cannot deactivate it. A plugin with Network: true in its header can only be activated this way.

Activated per site

Runs only where a site admin (if Menu Settings → Plugins allows it) or a super admin activated it; stored in that site’s active_plugins option.

Must-use (wp-content/mu-plugins/)

Runs on every site, cannot be deactivated, and only single files at the top level are loaded. Useful for network-wide policy (see Introduction and Architecture).

Lumen Press network-activates its catalogue plugin, because all three sites publish books, and lets Lumen Noir alone use an extra plugin:

wp plugin activate lumen-catalog --network
wp plugin activate akismet --url=lumenpress.test/noir/
wp plugin list --fields=name,status --url=lumenpress.test/noir/

The list reported lumen-catalog as active-network and akismet as active on Lumen Noir only; the network option active_sitewide_plugins then held lumen-catalog/lumen-catalog.php with its activation timestamp.

Network activation runs the plugin’s activation hook once, in the context of the current site, not on every site. A plugin that creates tables or roles on activation must loop over sites or create them lazily; that code is shown in Multisite Development.

Themes are not activated network-wide; they are network-enabled, which only makes them available. Each site still picks one theme on its own Appearance → Themes screen, which lists only enabled themes. The default theme is enabled when the network is created; others are enabled for the whole network or for one site in Sites → Edit → Themes:

wp theme enable lumen --network
wp theme enable lumen-child --network
wp theme activate lumen-child --url=lumenpress.test/kids/
wp network meta get 1 allowedthemes --format=json

The test network printed {"twentytwentyfive":true,"twentytwentyfour":true} for allowedthemes after enabling Twenty Twenty-Four, with the default theme already present. wp theme activate does not check that list: it switched Lumen Noir to a theme that was not enabled. Editing a theme’s files changes every site using it, which is why a child theme (lumen-child) per imprint is safer than editing lumen in place.

Users across sites

There is one wp_users table for the network. A user who is a member of a site has a role stored in wp_usermeta under that site’s prefix (wp_capabilities for the main site, wp_2_capabilities for Lumen Kids), plus primary_blog and source_domain. Being logged in is network-wide (subject to cookie scope), but being a member is per site: a logged-in user without a role on Lumen Noir who opened its dashboard got HTTP 403 and a page saying they do not currently have privileges on that site, with links to the dashboards of the sites they do belong to.

The commands below give maria.editor (ID 2), an account created on the single site before it was converted to a network, the Editor role on Lumen Kids and then take it away again:

wp user set-role maria.editor editor --url=lumenpress.test/kids/
wp user list --url=lumenpress.test/kids/ --fields=ID,user_login,roles
wp user remove-role maria.editor --url=lumenpress.test/kids/

set-role on a site where the user has no role adds them to it (Success: Added maria.editor (2) to https://lumenpress.test/kids as editor.); remove-role removes them from the site.

Removing a user from a site keeps the account and their posts; deleting a user is a network action (Network Admin → Users), which asks who should receive their content on each site. Marking a user as spam sets the spam column that wp_users gains on Multisite and blocks their login on every site.

New user names on a network are stricter than on a single site: wpmu_validate_user_signup() allows only lowercase letters and digits, at least four characters. wp user create maria.editor ... fails with Error: Usernames can only contain lowercase letters (a-z) and numbers. Accounts created before the conversion, such as Lumen Press’s maria.editor and tom.author, keep their names and work normally. New staff on a network need names such as mariaeditor, or a login by email address.

Registration and spam sign-ups

Allow new registrations (registration) takes four values:

Value Effect

none (default)

Only super admins (and site admins, if Add New Users is enabled) create accounts.

user

Visitors can register an account at wp-signup.php.

blog

Logged-in users can create new sites.

all

Visitors can register an account and a site at once.

With all, wp-signup.php on the test network offered a Username and Email Address and the choice Gimme a site! or Just a username, please. Sign-ups wait in wp_signups until the emailed link to wp-activate.php is followed; wp_registration_log records who created which site from which IP address. Per-site Anyone can register settings do not apply on a network: Settings → General hides the Membership field when Multisite is on.

Open registration on a network attracts automated sign-ups that create spam sites as well as spam accounts. Keep it at none unless the network exists to host other people’s sites, and if it must be open:

  • fill Banned Names (names that cannot be used as sites; the defaults include www, admin, files and blog) and Banned Email Domains, or restrict sign-ups to the organization’s domain with Limited Email Registrations

  • add an anti-spam or CAPTCHA plugin that supports wp-signup.php, network-activated

  • review new sites under Sites and mark abusive ones as spam (wp site spam <id>), which takes them offline

  • on a subdomain network, define NOBLOGREDIRECT so that unknown subdomains do not land on the sign-up form

Lumen Press keeps none: staff are added by super admins.

Domain mapping

Since WordPress 4.5, a site can use any domain without a plugin. The site’s domain and path are the domain and path columns of its wp_blogs row, and WordPress looks requests up there. The steps are:

  1. Point the new domain’s DNS at the server (an A/AAAA record, or a CNAME to the main host).

  2. Make the web server answer the name (ServerAlias or server_name) and install a certificate for it.

  3. In Network Admin → Sites → Edit, change Site Address (URL) on the Info tab and save.

For Lumen Noir, saving https://lumennoir.test on that screen updated the wp_blogs row and the site’s home and siteurl options together, and the new domain served the site:

blog_id	domain	path	url
1	lumenpress.test	/	https://lumenpress.test/
2	lumenpress.test	/kids/	https://lumenpress.test/kids/
3	lumennoir.test	/	https://lumennoir.test/

WP-CLI 2.12 has no command that changes a site’s domain. Updating only the options with wp option update home and siteurl is not enough: the test site then redirected lumennoir.test to the main site (no wp_blogs row matched) and answered 404 at its old path. A script must update the row with wp_update_site() (5.1+) and the two options:

# Map site 3 (Lumen Noir) to its own domain
wp eval 'wp_update_site( 3, array( "domain" => "lumennoir.test", "path" => "/" ) );'
wp option update home https://lumennoir.test --url=lumennoir.test
wp option update siteurl https://lumennoir.test --url=lumennoir.test
wp search-replace 'lumenpress.test/noir' 'lumennoir.test' 'wp_3_*' --skip-columns=guid \
  --url=lumennoir.test --dry-run

After the first command, --url uses the new domain, because WP-CLI finds the site the same way as a web request. Remove --dry-run from the last command once its report looks right; it rewrites links in that site’s content and leaves guid alone.

The old address does not redirect to the new one. On the subdomain test network, noir.lumenpress.test went to the sign-up page after mapping; on the subdirectory network, /noir/ returned 404. Add a permanent redirect in the web server for every old address that has been published.

Cookies on mapped domains

On a subdomain network COOKIE_DOMAIN is .lumenpress.test by default. A browser refuses that cookie on lumennoir.test, and the login form at lumennoir.test/wp-login.php failed with Error: Cookies are blocked or not supported by your browser. You must enable cookies to use WordPress. The fix from the domain mapping guide, placed after the network constants:

define( 'COOKIE_DOMAIN', $_SERVER['HTTP_HOST'] );

With it, the login worked on lumennoir.test, kids.lumenpress.test and lumenpress.test, each setting cookies for its own host. The cost is a separate login per site. PHP scripts that load wp-config.php without a web request (WP-CLI sets HTTP_HOST itself) see an undefined index warning; $_SERVER['HTTP_HOST'] ?? '' avoids it. On a subdirectory network cookies are host-only already, and mapped domains log in without this line.

A sunrise.php drop-in left over from the old domain-mapping plugin can intercept requests; remove it, and the SUNRISE constant, before relying on native mapping.

Wildcard DNS and TLS

A subdirectory network needs only the main host’s DNS record and certificate. A subdomain network needs a record for every site; a wildcard record lets new sites work without DNS changes:

; zone lumenpress.example (addresses are documentation ranges)
lumenpress.example.     3600 IN A     203.0.113.10
*.lumenpress.example.   3600 IN CNAME lumenpress.example.
; zone lumennoir.example (a mapped imprint)
lumennoir.example.      3600 IN A     203.0.113.10

A certificate for *.lumenpress.example covers one level only (kids.lumenpress.example, not www.kids.lumenpress.example) and not the bare lumenpress.example, so request both names. Let’s Encrypt issues wildcard certificates only through the DNS-01 challenge, which needs API access to the DNS provider; HTTP-01 cannot be used for them. Each mapped domain needs its own certificate (served with SNI on the same IP address). The web server must also accept the wildcard: ServerAlias *.lumenpress.example in Apache, server_name *.lumenpress.example in nginx. Lumen Press avoids all of this by using subdirectories plus one mapped domain with its own certificate.

Backing up and moving a network

A backup of a network is a backup of the whole database and the whole wp-content directory, including uploads/sites/. wp db export without options exports every table registered on the network; a single site’s tables can be exported for a partial restore:

wp db export lumen-network.sql
wp db export lumen-kids.sql \
  --tables="$(wp db tables --url=lumenpress.test/kids/ --scope=blog --format=csv)"

On the test network the first file held 38 CREATE TABLE statements and the second 10. A single site’s export does not include its users (they are in the global tables) or its wp_blogs row; restore it only into the same network. Retention, off-site copies and restore drills are on Backups and Disaster Recovery.

Moving a network to a new domain (for example from staging lumenpress.test to production lumenpress.example) touches DOMAIN_CURRENT_SITE, the wp_site and wp_blogs rows, and every site’s URLs. Run a dry run first:

wp config set DOMAIN_CURRENT_SITE lumenpress.example
wp search-replace 'lumenpress.test' 'lumenpress.example' --network \
  --skip-columns=guid,user_email --dry-run --report-changed-only
Table	Column	Replacements	Type
wp_2_options	option_value	3	PHP
wp_blogs	domain	2	SQL
wp_options	option_value	3	PHP
wp_site	domain	1	SQL
wp_sitemeta	meta_value	2	PHP
...

--network is what includes the tables of every site and the network tables. Search for the bare domain, not //lumenpress.test: the domain columns of wp_blogs and wp_site hold names without a scheme, and a pattern with slashes skips them, leaving a network that cannot find its own sites. Without --skip-columns=user_email the run also rewrites staff email addresses that use the old domain. When wp search-replace itself fails because the network cannot boot under the old URL, pass --url= with the old main address. The general migration workflow is on Importing, Exporting and Migrating.

Common errors and pitfalls

Symptom Cause and fix

Main site works, other sites return 404 or the main site’s 404 page

Rewrite rules missing: the single-site .htaccess block is still in place, AllowOverride is None, or nginx lacks the subdirectory rewrites. Install the network rules above.

"One or more database tables are unavailable. The database may need to be repaired."

On Apache, FollowSymLinks/SymLinksIfOwnerMatch is off so RewriteRule is forbidden; otherwise a network table is missing or wp-config.php constants do not match wp_site/wp_blogs.

"Error establishing a database connection" right after enabling the network

DOMAIN_CURRENT_SITE or PATH_CURRENT_SITE does not match a row in wp_blogs, or code was added after the require_once ABSPATH . 'wp-settings.php'; line. Fix the constants; move the code above the "stop editing" line.

Login loops, or Cookies are blocked or not supported by your browser on a mapped domain

Cookie domain does not match the host. Define COOKIE_DOMAIN as shown above; clear old cookies.

Sub-domains option missing on Network Setup

The address is localhost, an IP address, or has a path. Use a real host name.

Sub-directories option missing

Published posts older than a month. Define ALLOW_SUBDIRECTORY_INSTALL after planning redirects, or use subdomains.

Old post URLs of the main site return 404 after conversion

The forced /blog/ prefix. Redirect /<post-name>/ to /blog/<post-name>/, or keep the main site’s posts under a page tree.

A site admin cannot activate plugins, add users or paste embed scripts

Expected on a network. Enable Menu Settings → Plugins or Add New Users; unfiltered_html stays with super admins.

The handbook’s network debugging guide also lists the SQL to recreate missing wp_site, wp_blogs and site_admins rows by hand.

Where older books are outdated

Older claim Current behaviour (WordPress 7.1)

Mapping a domain to a network site needs the WordPress MU Domain Mapping plugin, or a host’s own portal

Native since 4.5: change the Site Address on Network Admin → Sites → Edit, plus DNS, a certificate and, on subdomain networks, COOKIE_DOMAIN

A network has a fixed number of tables, as counted in the book

Six network tables, the 12 main-site tables (users included) and ten tables per extra site; wp_blogmeta exists since 5.1 and termmeta since 4.4

Multisite needs more memory, with the book’s figure for the default

WP_MEMORY_LIMIT defaults to 64M on Multisite (40M single site); WP_MAX_MEMORY_LIMIT to 256M

Apache rules with Order allow,deny and Allow from all

Apache 2.4 uses Require all granted

Uploads live in wp-content/blogs.dir and are served through ms-files.php

Only for networks created before 3.5; newer networks use wp-content/uploads/sites/<id>/

Multisite Development (switch_to_blog(), network options, activation across sites, WP_Site_Query), Database Schema, wp-config.php Reference, Web Servers: Apache and nginx, Users, Roles and Profiles, Security Hardening, Scaling and High Availability, Debugging, Site Health and Troubleshooting and WP-CLI. Network activation and per-site uninstall of WooCommerce extensions are covered in Extension Development.

References