Facing downtime, SEO drift, or tangled user IDs when separating a subsite from a WordPress network?
Digital entrepreneurs, sysadmins, and small teams must weigh speed, uptime, cost, and data risk before pulling a site out.
One misstep can break links, users, or SSL and slow recovery.
Split WordPress Multisite into independent sites without downtime.
Do this by extracting site tables, media, and users.
Rewrite serialized URLs, update file paths and DNS.
Then verify SEO and SSL.
Include ready-to-run WP-CLI and SQL scripts and user-ID mapping.
Prepare a rollback plan and post-migration tests.
Compare costs and tools to measure risk.
Summary of the process
List the full process in order with expected time and outcome.
The migration extracts the wp_N_ tables for the site.
It exports users tied to site N.
It syncs uploads to the new host.
It imports into a fresh single-site install.
Run a serialized-safe search-replace after import.
Remap user IDs when needed.
Perform DNS and SSL cutover at the end.
Expect a dry-run of 1–4 hours on staging for small-to-medium sites.
This applies to sites under about 10k posts.
Plan a live cutover window of 2–30 minutes depending on DNS TTL and CDN cache.
If TTL equals 60 seconds and certificates are pre-provisioned, most cutovers finish in 10–15 minutes.
Allow a conservative 30-minute window for verification and remediation.
This section gives the quick checklist for planning and the key checkpoints to automate before cutover.
Key steps
-
Identify site ID N from network tables and snapshot the entire multisite database and uploads (RPO = snapshot age).
-
Dump only the wp_N_ tables and any global term tables needed, then rename prefixes for single-site import.
-
Export users tied to site N, handle ID collisions, and import while preserving password hashes and roles.
Estimated time and downtime
Dry-run on staging: 1–4 hours depending on media size and DB complexity.
DNS sequencing with TTL=60s yields a typical cutover propagation of 2–10 minutes.
Certificate issuance can add 2–15 minutes unless pre-provisioned.
Check each step before moving to the next one.
Export DB and import steps
Export the site-specific DB tables and prepare them for import.
Begin by locating the site ID in the network.
Run: SELECT blog_id FROM wp_blogs WHERE domain='old.example.com' LIMIT 1; use that ID as N.
Always take a full DB snapshot before any extracts.
Run: mysqldump --single-transaction --routines --triggers -u user -p dbname > full_backup.sql.
Dump and rename tables
Dump only the site tables for site N (example N=3):
bash
mysqldump -u dbuser -p --single-transaction --quick dbname /
wp_3_posts wp_3_postmeta wp_3_terms wp_3_term_taxonomy /
wp_3_term_relationships wp_3_comments wp_3_commentmeta wp_3_options > site3_tables.sql
Replace table prefixes to match a single-site import without altering serialized data:
bash
sed 's/wp_3_/wp_/g; s/INSERT INTO wp_3_/INSERT INTOwp_/g' site3_tables.sql > site3_tables_renamed.sql
Run a quick schema check.
Use: grep "CREATE TABLE" site3_tables_renamed.sql | wc -l and compare to a fresh wp schema.
Serialized-safe search-replace
Import into a fresh WP instance first.
Then use WP-CLI for URL fixes because WP-CLI handles PHP serialization safely.
Import command:
bash
mysql -u dbuser -p new_dbname < site3_tables_renamed.sql
Run a dry-run search-replace to preview changes.
Bash
wp search-replace 'https://old.example.com' 'https://new.example.com' --skip-columns=guid --precise --recurse-objects --dry-run
Inspect the dry-run output before the real command.
Bash
wp search-replace 'https://old.example.com' 'https://new.example.com' --skip-columns=guid --precise --recurse-objects
The most frequent error here is running naive text replacements on SQL dumps and corrupting option lengths.
Serialized strings change length and break widgets and menus.
Use WP-CLI for search-replace when serialized data exists; do a dry-run and save its output for rollback verification.
| Step |
Command |
Why |
| Dump site tables |
mysqldump ... Wp_N_posts ... |
Extract only site data |
| Rename prefixes |
sed 's/`wp_N_/`wp_/g' ... |
Prepare for single-site schema |
| Serialized replace |
wp search-replace ... --recurse-objects |
Avoid corrupting PHP-serialized values |
1. Snapshot DB & files
→
2. Dump wp_N_ tables
→
3. Import into new WP
→
4. WP-CLI search-replace
→
5. Sync uploads & verify
Operators should prepare a short extraction and transform script that wraps mysqldump of the site-specific tables and imports into a temporary database. Then perform SQL-based table copy and rename to safe wp_ names and export term tables needed for taxonomy integrity.
Consider an alternate path using WP-CLI to export and run WP-CLI search-replace for serialized data. The example sequences reduce the temptation to run global sed on dumps and make the flow reproducible.
Users, ID mapping and passwords
Export linked users, map IDs and preserve password hashes and roles.
Find all users associated with site N by inspecting wp_usermeta keys that include the site prefix.
Export only those IDs for migration.
Example query to list user IDs for site 3:
sql
SELECT DISTINCT user_id FROM wp_usermeta WHERE meta_key = 'wp_3_capabilities';
Dump the wp_users and wp_usermeta rows for those IDs.
Bash
mysqldump -u dbuser -p --no-create-info --skip-triggers --where="ID IN (1,5,23)" dbname wp_users > users.sql
mysqldump -u dbuser -p --no-create-info --skip-triggers --where="user_id IN (1,5,23)" dbname wp_usermeta > usermeta.sql
If user IDs are many, script the ID list generation and feed it into mysqldump.
This usually takes 10–90 seconds for 100–1,000 users.
Detect duplicates and decide merge
Run this to detect login collisions on the target site:
sql
SELECT u.user_login, u.ID FROM wp_users u WHERE user_login IN (SELECT user_login FROM source_db.wp_users WHERE ID IN (1,5,23));
If collisions exist, choose between merging accounts or creating new accounts with modified logins.
A common hidden problem is mismatched capabilities stored in meta keys prefixed with wp_N_.
Those keys must be renamed to match the target site's prefix.
Remap posts
If new IDs differ from old IDs, build a mapping table and update references.
Sql
CREATE TABLE user_map (old_id INT PRIMARY KEY, new_id INT);
-- Fill user_map by matching by user_login after import
UPDATE wp_posts p JOIN user_map m ON p.post_author = m.old_id SET p.post_author = m.new_id;
UPDATE wp_comments c JOIN user_map m ON c.user_id = m.old_id SET c.user_id = m.new_id;
UPDATE wp_usermeta um JOIN user_map m ON um.user_id = m.old_id SET um.user_id = m.new_id;
Preserve password hashes by inserting wp_users rows that include the user_pass column.
MySQL will accept the hash and WordPress will authenticate users without forcing a reset.
When IDs conflict, import users without IDs into a temporary table and then match by user_login. Build a mapping table and run the remap queries above to avoid orphaned posts.

Use a concrete user-migration pattern that detects collisions and preserves password hashes; the pattern should emit a deterministic user_map for remapping. For example:
- Generate the export list: SELECT ID, user_login, user_email FROM source.wp_users JOIN source.wp_usermeta USING (ID) WHERE meta_key='wp_3_capabilities'
- Import users into a temp table on target and run a matching pass by user_login to detect collisions
- If collisions exist, run a merge policy: match by email first, then user_login; otherwise append suffix and record new login
- Build user_map as a simple two-column table (old_id,new_id) and run automated remap queries as shown above
Using exact SQL snippets and a small bash wrapper to loop pages of IDs makes remapping repeatable and reduces human error during the migration.
Copy uploads, preserve ownership and fix paths so images and attachments load correctly.
Multisite stores files under wp-content/uploads/sites/N/.
The target single-site path is wp-content/uploads/ by default.
Use rsync to move files and preserve permissions and timestamps.
For large media sets this step often takes the longest. The time ranges from minutes to hours depending on size and bandwidth.
Rsync with correct ownership and perms
Example rsync command (adjust web user and paths):
bash
rsync -avz --progress --chown=www-data:www-data /var/www/html/wp-content/uploads/sites/3/ /var/www/newsite/wp-content/uploads/
find /var/www/newsite/wp-content/uploads -type d -exec chmod 755 {} /;
find /var/www/newsite/wp-content/uploads -type f -exec chmod 644 {} /;
Verify file counts match:
bash
find /var/www/newsite/wp-content/uploads | wc -l
find /var/www/html/wp-content/uploads/sites/3 | wc -l
Run WP-CLI replace for uploads path changes.
Bash
wp search-replace 'https://old.example.com/wp-content/uploads/sites/3' 'https://new.example.com/wp-content/uploads' --precise --recurse-objects
Then regenerate attachment metadata for missing sizes if needed.
Bash
wp media regenerate --yes
A common trap is copying files but not preserving ownership.
That causes 403 errors when PHP-FPM serves files.
Always chown to the web user and test a sample image path via browser and curl.
Cutover, verification and rollback
Perform DNS cutover, verify the site, and have an automated rollback ready.
Reduce DNS TTL to 60 seconds at least 48 hours before cutover to minimize propagation lag.
Prepare TLS certificates in advance to avoid delays.
Pre-issue a certificate via Let's Encrypt or request a managed cert from the host.
Automated verification checklist
Include these executable checks as scripts and run them immediately after cutover:
-
HTTP health: curl -s -o /dev/null -w "%{http_code}" https://new.example.com (expect 200 or 301)
-
Sitemap presence: curl -s https://new.example.com/sitemap.xml | grep -E '' | head
-
Canonical tag check: curl -s https://new.example.com | grep 'rel="canonical"'
-
DB row counts: SELECT COUNT(*) FROM wp_posts; compare to source site counts.
-
File checksum parity: generate MD5 lists on source and target and diff them.
Script example (pseudo):
bash
if [ $(curl -s -o /dev/null -w "%{http_code}" https://new.example.com) -ne 200 ]; then echo "HTTP check failed"; exit 1; fi
Rollback sequencing
-
Prepare backups: snapshot DB, export full mysqldump and create filesystem snapshot before cutover.
-
If rollback needed, re-point DNS back to the original IP and restore DB and uploads from pre-cutover snapshots.
-
Clear CDN caches and flush server caches.
Restoration example:
bash
mysql -u dbuser -p original_dbname < backup_pre_cutover.sql
rsync -avz /backup/uploads_snapshot/ /var/www/html/wp-content/uploads/
The expected RTO depends on snapshot restore speed.
For medium sites expect 30–90 minutes to fully restore from snapshots if media must be synced back.
Expand the rollback playbook into an actionable, timed plan with verification steps and RTO/RPO guidance.
Example plan: snapshot DB and uploads with timestamps, set DNS TTL to 60s, and take a second incremental DB snapshot immediately before cutover.
If a rollback is required within the first hour, sequence these steps:
- Re-point DNS to source IP (expected DNS propagation 1–5 minutes with TTL=60)
- Restore pre-cutover DB snapshot (mysql < backup_pre_cutover.sql) and rsync uploads from the pre-cutover filesystem snapshot
- Purge CDN and server caches
- Run automated verification scripts and only then reopen write access
Estimated RTO examples: small site 15–45 minutes; medium site with large media 30–120 minutes.
Include a short rollback-checklist script that runs curl, wp-cli DB size checks, and an md5sum diff to validate restoration success.
Errors that ruin the migration
Avoid the most common mistakes that corrupt data, break users, or damage SEO.
Running a naive search-and-replace on SQL dumps corrupts PHP-serialized values and often breaks widgets, menus, and plugin settings.
Not reconciling user IDs causes orphaned posts or posts assigned to the wrong author.
Serialized corruption
The error pattern looks like this: text lengths in serialized strings change and unserialize() fails.
That leads to empty widgets and broken options.
The fix is to avoid text-based replace on SQL and to do replacements inside WordPress using WP-CLI or PHP scripts that reserialize correctly.
User ID mismatches
If user IDs are not remapped, posts refer to post_author IDs that may belong to other accounts on the target site.
A common case is posts showing as 'admin' because old IDs matched a generic admin ID on the target.
The fix is to build the user_map and run the update queries shown earlier.
The hidden cost in many projects is time to debug these two failures.
Resolving a serialized corruption typically takes 2–6 hours of debugging depending on plugin complexity.
Compare the manual approach with common plugins and managed migrations to pick based on cost, time and risk.
The manual approach (WP-CLI + SQL + rsync) has low direct cost but requires sysadmin time.
Migration plugins reduce manual steps but add license fees and often miss edge cases.
Managed host migrations cost more but include support, backups, staging and SLA-based uptime guarantees.
Practical comparison table
| Option |
One-time cost |
Time |
Risk |
Best for |
| Manual (WP-CLI + SQL) |
$0–$300 (tools) |
3–10 hours |
Medium (depends on expertise) |
Teams with sysadmin skills |
| WP Migrate / All-in-One |
$99–$249 license |
1–4 hours |
Medium (may miss custom meta) |
Small teams preferring GUI |
| Managed host migration |
$0–$500 migration fee |
1–8 hours |
Low (vendor handles edge cases) |
High uptime requirements |
Typical managed hosts advertise SLAs around 99.95%; choose managed hosting when uptime and support time outweigh higher monthly cost.
If the subsite is tied to cross-site single sign-on, shared plugin settings, or network-wide roles, do not split. Consider per-site containerization or keeping the network and scaling the node instead.
Consider requesting a pre-cutover audit.