WordPress Migration and Multisite: Advanced Site Architecture

WordPress Migration and Multisite: Advanced Site Architecture

Last verified: October 13, 2026
18 min read
Guide
500+ WP projects

Two technical challenges frequently cause anxiety for WordPress site administrators and developers: executing a flawless server migration without downtime, and architecting a WordPress Multisite network that scales cleanly without collapsing under maintenance debt.

When handled carelessly, both procedures carry severe business risks. A botched database search-replace breaks serialized arrays, leaving themes and custom fields broken. Similarly, choosing Multisite for an incompatible project structure creates an architectural bottleneck where a single plugin conflict or PHP fatal error takes down dozens of web properties simultaneously.

In this technical guide, we break down the engineering principles behind reliable zero-downtime migrations using WP-CLI and explore the multi-tenant architecture of WordPress Multisite. Migration and Multisite are two separate decisions that often get mixed up: a migration moves files, database and DNS, while Multisite changes how several sites share one WordPress codebase. The official reference for the network side is the WordPress Multisite handbook.

For more insights into our production infrastructure and development practices, explore our WordPress development services.

#Preserving database integrity during WordPress migrations

The primary reason WordPress migrations fail is not missing files; it is corrupted database records. WordPress stores a substantial portion of theme options, widget configurations, and plugin data (such as Advanced Custom Fields or page builder layouts) as serialized PHP strings.

#Why a SQL replace breaks serialized PHP data

Consider how WordPress stores configuration data inside the wp_options table:

a:2:{s:4:"host";s:14:"old-domain.com";s:4:"port";i:3306;}

In this string, s:14 explicitly declares that the string old-domain.com is exactly 14 characters long. If you open a raw .sql file in a generic text editor or run a crude UPDATE wp_options SET option_value = REPLACE(...) query to swap the domain to example-shop.com (16 characters), the length declaration remains s:14.

When WordPress next queries the database and calls unserialize(), the PHP parser encounters 16 characters instead of the promised 14. The parser throws an unserialize(): Error at offset notice, fails closed, and discards the entire array. As a result, widgets vanish, layout options revert to defaults, and API keys disappear.

#How WP-CLI search-replace fixes serialized data

The official WordPress command-line interface (WP-CLI) natively handles serialization. It pulls objects into memory, deserializes them into PHP data structures, performs the string replacement, recalculates string lengths, and re-serializes the data before committing changes to MySQL. The search-replace reference also states that it never changes primary key values. Keep the dry run identical to the real run, so the preview shows exactly what will change:

# 1. Dry run: same tables and flags as the real run, nothing is written
wp search-replace 'https://old-domain.com' 'https://new-domain.com' \
  --all-tables --skip-columns=guid --dry-run

# 2. Execute the replacement
wp search-replace 'https://old-domain.com' 'https://new-domain.com' \
  --all-tables --skip-columns=guid

# 3. Flush object cache and regenerate rewrite rules
wp cache flush
wp rewrite flush --hard

Two flags often copied from old tutorials need a correction. --recurse-objects is already the default, so adding it changes nothing. --precise is not what makes serialized data safe: by default the command runs fast SQL queries and switches to PHP on its own for columns that contain serialized data, while --precise forces PHP on every column, which is slower and only worth it for unusually complex structures. --skip-columns=guid matters more, for the reason explained in the site URL section below.

#How to migrate WordPress with zero downtime

To move high-traffic e-commerce stores or lead generation portals without dropping transactions or confusing search engine crawlers, adhere to this four-phase operational sequence:

#Phase 1: DNS TTL reduction (48 hours prior)

Lower the Time to Live (TTL) on your domain’s DNS A records to 300 seconds (5 minutes). This ensures that global DNS caches refresh rapidly when the new server IP is published.

#Phase 2: Incremental file sync with rsync

Traditional FTP is slow, lacks encryption, and fails silently on large media libraries. Use rsync over SSH to mirror files directly between servers, excluding transient cache directories:

rsync -avzP --delete \
  --exclude 'wp-content/cache/*' \
  --exclude 'wp-content/uploads/cache/*' \
  --exclude '*.log' \
  [email protected]:/var/www/vhosts/site.com/public/ /var/www/html/

The -a flag preserves file permissions and timestamps, while -z compresses data over the wire.

#Phase 3: Cutover window

  1. Enable maintenance mode on the source server: wp maintenance-mode activate.
  2. Generate a clean database dump: wp db export cutover.sql --add-drop-table.
  3. Transfer and import the dump onto the new host: wp db import cutover.sql.
  4. Run wp search-replace to update site URLs, SSL protocols, or path differences.
  5. Verify site health locally by mapping the domain to the new IP in your local hosts file.

#Phase 4: DNS switch and verification

Update the DNS A record to point to the new server IP. Because the TTL was reduced to 300 seconds in Phase 1, traffic migrates smoothly to the new host within minutes. Once DNS has fully propagated globally, restore standard TTL (86400 seconds).

#How WordPress multisite works for multiple sites

WordPress Multisite enables organizations to manage hundreds or thousands of virtual websites from a single codebase and database instance.

#WordPress multisite database tables

A standard WordPress install contains 12 core tables. In Multisite, WordPress separates global shared data from tenant-specific data:

  • Global Network Tables:
    • wp_users and wp_usermeta: User accounts exist globally once across the network. A user can be an Author on Site 1 and an Administrator on Site 2.
    • wp_blogs, wp_site, wp_sitemeta: Track network-level settings, registration policies, and active sites.
  • Tenant-Specific Tables:
    • Each child site receives an isolated set of content tables identified by site ID: Site 2 operates on wp_2_posts, wp_2_options, and wp_2_comments.
+-----------------------------------------------------+
|             WordPress Multisite Schema              |
+-----------------------------------------------------+
| Network Global Tables:                              |
| - wp_users / wp_usermeta                            |
| - wp_blogs / wp_site / wp_sitemeta                  |
+-----------------------------------------------------+
| Primary Site (ID 1):                                |
| - wp_posts, wp_options, wp_terms...                 |
+-----------------------------------------------------+
| Subsite (ID 2 - e.g. london.brand.com):             |
| - wp_2_posts, wp_2_options, wp_2_terms...           |
+-----------------------------------------------------+
| Subsite (ID 3 - e.g. paris.brand.com):              |
| - wp_3_posts, wp_3_options, wp_3_terms...           |
+-----------------------------------------------------+

#When to use WordPress multisite

  1. Franchise and Regional Networks: When 100 regional dealerships share the same theme, approved plugins, and core branding, differing only in inventory and localized contact details.
  2. Higher Education and Institutional Portals: Giving academic departments and student organizations standardized blogs under an overarching institutional domain.
  3. Internal Staging and Product Demos: Instantly provisioning sandboxed sites with pre-installed configurations via automated scripts.

Multilingual content alone is rarely a reason for Multisite. WPML, Polylang or language variants inside one installation are often simpler to run than a site-per-language network. The handbook makes a related point: if the sites must share data or users heavily, or only need different themes per section, a single site with a plugin may fit better than a network.

#When to avoid WordPress multisite

  • Diverse Agency Client Rosters: One poorly coded plugin or memory leak on Client A’s subsite can crash the entire PHP process pool, bringing down Clients B, C, and D.
  • Divergent Plugin Requirements: If site A requires WooCommerce with complex ERP connectors and site B requires an advanced forum, maintaining a shared plugin directory becomes fragile and prone to dependency conflicts.
  • Separate hosting needs: When sites need different PHP versions, different hosting plans or different security zones, one network cannot give them that.
  • Future Extraction Complexity: Decoupling a single subsite from a Multisite database into an independent standalone site requires exporting filtered SQL tables, rewriting table prefixes, and re-associating users.

#Requirements before creating a WordPress multisite network

The Before you create a network chapter lists restrictions that the Network Setup screen enforces, and most failed conversions run into one of them.

Network creation is blocked when the WordPress address uses a port other than 80 or 443. A sub-domain install is blocked when the WordPress URL contains a path (WordPress is not at the document root), when the address is localhost, or when it is an IP address such as 127.0.0.1. For local testing the handbook suggests mapping a custom hostname to 127.0.0.1 in the hosts file instead. A sub-directory install is blocked when the existing installation is more than a month old, because the main site’s posts would clash with subsite paths; on a path-based network the main site’s post URLs also get a /blog/ prefix.

Server side, Apache needs mod_rewrite with .htaccess support, FollowSymLinks not disabled, and AllowOverride set to All or Options All for the vhost. Pretty permalinks must already work on the single site. Settle the domain, including the www question, before activation, and if WordPress should run from its own directory, make that change first too. Wildcard DNS is only required when users create sub-domain sites on demand; sites added by hand can use explicit DNS records or virtual hosts.

#How to create a WordPress multisite network step by step

The Create a network procedure has an order that matters. Back up the database and files. Deactivate all plugins; they can be reactivated once the network exists. Then add the switch to wp-config.php above the line /* That's all, stop editing! Happy publishing. */:

// Step 1: Enable the network setup menu in the WordPress dashboard
define('WP_ALLOW_MULTISITE', true);

Navigate to Tools > Network Setup in the dashboard, choose between Sub-domains (subsite.domain.com) or Sub-directories (domain.com/subsite/), and paste the generated constants into wp-config.php directly after the WP_ALLOW_MULTISITE line:

// Step 2: Activate the production network configuration
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', true);
define('DOMAIN_CURRENT_SITE', 'domain.com');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);

The same screen prints rewrite rules. They replace the existing WordPress block in .htaccess (on Nginx they become server block rules). Back up both files before editing them. After saving, log in again through the link WordPress shows; clearing browser cookies is sometimes needed. “My Sites” then gains a Network Admin menu. Switching between sub-domains and sub-directories later is possible, but the handbook warns that it might not be easy, so choose before the first subsite exists.

#Domain mapping in multisite without a plugin

Since WordPress 4.5, native domain mapping is baked directly into the core engine. You can map custom top-level domains (e.g. clientdomain.com) directly within Sites > Edit Site in the Network Admin panel without installing third-party mapping plugins. On the web server layer, configure your Nginx or Apache server blocks to accept requests for mapped domains and forward them to the primary WordPress root.

The domain mapping chapter adds the order of work. Point every domain at the server in DNS first. Install TLS for the primary domain and use SNI certificates for the others, so every admin login stays encrypted. Then enter the full URL, for example https://clientdomain.com, in the Site Address (URL) field of that subsite. If logging in on a mapped site fails with a blocked-cookies error or silently, add this line after the network constants:

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

When moving a site to its own domain, lower the TTL a day ahead, make sure the server answers on the new hostname, and only then change the Site Address. Run a serialization-safe search-replace for old URLs in posts, media and options afterwards.

#User roles across sites in WordPress multisite

In Multisite, users live in one shared table. Membership and roles are assigned per site.

RoleTypical responsibilityScope
Super AdminPlugins, themes, new sites, network settingsWhole network
AdministratorContent and users on their siteOne site
Editor / AuthorEditorial contentOne site
SubscriberLogin or membershipPer site

What that means in practice:

  • An Administrator on one subsite cannot install plugins. That is a Super Admin job.
  • Deleting a user from Network Admin removes the account everywhere, not only on one branch site.
  • Single sign-on across several brands comes naturally with Multisite; isolation between clients does not.

Document who holds Super Admin and keep the number small. Too many Super Admins is the usual reason behind “someone activated a plugin in Network Admin and three sites went down”.

#How to merge WordPress sites into multisite

Enabling a network on an existing installation, as described above, is not the same as merging five single installations. Every additional site has to come in through export and import (WXR), WP-CLI, or a tool that understands Multisite table prefixes.

Recommended order for a merge:

  1. Take a full file and database backup of every source site (3-2-1: three copies, two media, one off-site).
  2. Build the network on staging with the same PHP version as production.
  3. Create empty sites in Network Admin with their final domains or sub-domains.
  4. Import content site by site. Then run search-replace for old URLs inside that site’s table set only, using --url=<site> without --network.
  5. Map users: shared email addresses become one account; roles must be assigned per site.
  6. Verify media. Uploads for a subsite live under wp-content/uploads/sites/{id}/.
  7. Lower DNS TTL, switch, and keep rollback SQL ready.

#How to move one site out of WordPress multisite

Sometimes the network is the wrong model. In that case you export one site out:

  1. Identify the blog_id and table prefix (wp_5_ and so on), for example with wp site list --fields=blog_id,url.
  2. Export the content (WXR) or clone the tables into a new database with the standard wp_ prefix.
  3. Copy media from uploads/sites/{id}/ to the regular uploads/ folder.
  4. Update siteurl and home, then run search-replace.
  5. Install only the plugins and themes the site actually used, not the whole network directory blindly.

Plan redirects from the old sub-domain URLs to the new top-level domain. Otherwise you lose link equity and get 404 noise in Search Console.

#How to change the WordPress site URL after a move

Settings > General holds two fields, WordPress Address (URL) for where the core files live and Site Address (URL) for the address visitors type. When a move leaves the admin unreachable, the migration chapter documents four ways to fix them. These are for single installs; on Multisite the handbook sends you to the database, covered in the next section.

  1. wp-config.php: define( 'WP_HOME', 'https://example.com' ); and define( 'WP_SITEURL', 'https://example.com' );. This hard-codes the values, and the General settings fields can no longer be edited.
  2. functions.php: two update_option() calls for siteurl and home in the active theme, loaded once, then removed. The handbook is explicit that the code must not stay in the file.
  3. RELOCATE: define('RELOCATE', true); in wp-config.php, then open wp-login.php on the new address. It only fixes the Site URL, nothing else, and leaving it in place is insecure because in some configurations it lets an attacker change the site URL. Remove it right after.
  4. Database or WP-CLI: edit siteurl and home in wp_options, or run wp option update home 'https://example.com' and wp option update siteurl 'https://example.com'.

The same chapter has a rule worth repeating: never change the guid column in wp_posts. Feed readers use the GUID to recognise posts they have already shown, so rewriting it makes old posts reappear as new. That is why the WP-CLI example in the docs uses --skip-columns=guid. Cached copies of the old address can also sit in transients; they are safe to delete in the migrated copy (wp transient delete --all) because WordPress rebuilds them on demand.

Moving WordPress to another directory on the same server has its own order: change both URLs in Settings first, then move the files. If the files moved first, the handbook’s workaround is a temporary symlink from the old path to the new one (ln -s /path/to/new /path/to/old), after which the normal steps work.

#How to run wp search-replace on a multisite network

On Multisite, wp search-replace only touches the current site’s tables unless you say otherwise. The command reference defines the scopes:

  • no flag: tables registered to $wpdb for the site given by --url (the main site by default),
  • --network: all tables registered to $wpdb across the network,
  • --all-tables-with-prefix: any table with the WordPress prefix, registered or not,
  • --all-tables: every table in the database, whatever its prefix. It overrides --network, so on a database shared with other applications it reaches their tables too.

Tables without a primary key are skipped silently; wp db query 'DESCRIBE <table>' shows whether PRI is present. The documented way to turn a production network into a local copy also rewrites the network tables explicitly:

wp search-replace --url=example.com example.com example.test \
  'wp_*options' wp_blogs wp_site --network --skip-columns=guid

--export=network.sql writes the transformed data to a file instead of the live database, which suits a cutover where the import happens on the new host. Before and after the change, list every site with wp site list and compare the stored addresses:

for url in $(wp site list --field=url); do
  echo "$url $(wp option get siteurl --url="$url")"
done

The handbook’s Multisite move notes set the scope. Same domain on a new server: copy files and database exactly as for a single install. New domain: move the files, update DOMAIN_CURRENT_SITE in wp-config.php and .htaccess if the folder changed, then review wp_site and wp_blogs (domain and path) and, in every wp_N_options table, home, siteurl and fileupload_url. Switching between sub-domains and sub-directories also means changing SUBDOMAIN_INSTALL and the rewrite rules.

#Common WordPress multisite staging mistakes

Staging is where Multisite most often goes wrong, not production.

URLs hard-coded in the network. A cloning tool that only swaps the main domain but leaves wp_blogs and wp_site pointing at production logs editors into the wrong environment or mixes cookies.

Search-replace without excluding the GUID or without a dry run. Always run the dry run. Limit the tables when you only move one site.

Cron and email. Network cron (wp-cron.php) and outgoing mail from staging must not reach real customers. Block outgoing SMTP on staging or route it to a mail sink.

Staging sub-domains. staging.brand.com needs its own DNS and usually its own TLS certificate. A wildcard certificate for *.brand.com does not cover staging.otherbrand.com. Test HTTPS before inviting content editors.

Network-level plugin activation. A test plugin on staging is fine. The same action in production without a canary site is not. Keep at least one canary site where new plugins are activated first.

WooCommerce in a network. If the network is a chain of shops, test payment gateway callbacks per site URL. Webhooks that still point at staging after go-live are expensive. Without e-commerce, keep the migration to content, media and roles.

#Multisite, plain migration or a separate install

SituationRecommendationWhy
8 franchise locations, same themeMultisite with sub-domains or mappingShared plugins, shared login
5 clients in different industriesSeparate installationsFault isolation and legal separation
One company, three languagesOften one install plus a language pluginSimpler SEO and menus than three sites
University with student blogsMultisiteScalable site creation
Changing hosting, keeping the modelPlain migration (files, DB, DNS)Multisite is irrelevant to the move itself
One brand leaving the networkExport one site to a single installSmaller blast radius

#What belongs on a WordPress migration checklist

  1. Backup verified with a restore test, not just a file size.
  2. Same PHP version and extensions on staging and production.
  3. wp search-replace --dry-run without surprises, with the same flags as the real run.
  4. Login tested as Super Admin and as a site Administrator.
  5. Media URLs load over HTTPS on every mapped domain.
  6. wp site list --field=url matches the expected list of addresses.
  7. DNS TTL lowered; rollback plan written down (who flips the A record, where the previous SQL dump lives).

Migration is mechanics. Multisite is an operating model. Mix them up and you end up fixing DNS when you should have separated the clients, or the other way round.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If you want to convert the article into a working site improvement, redesign, or build plan, I can define the scope and implement it.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Frequently asked questions

Practical answers to apply the topic in real execution.

Why does editing a SQL dump in a text editor break WordPress settings?#
WordPress stores complex options and widget data as serialized PHP arrays where each string is prefixed by its exact character length. If you replace an old domain with a new domain of a different length, the byte counter no longer matches. PHP fails to deserialize the array and discards the entire option, resetting plugins to their default states.
When should you choose WordPress Multisite over separate single installs?#
Multisite is ideal for networks of related sites that share themes, core plugins, and centralized governance: franchise branches, university departmental pages, or international brand variations. Avoid Multisite for unrelated client sites where a fatal PHP error on one site could bring down the entire network.
How do you achieve a zero-downtime migration for an active website?#
Lower DNS TTL to 300 seconds 48 hours in advance, pre-sync media assets via rsync over SSH, activate maintenance mode for a final database export, import and run WP-CLI search-replace on the destination server, and point the DNS A record to the new IP address.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles