Converting a fresh WordPress install to Multisite is fairly easy. Converting an existing production site is different.
The Multisite switch itself takes only one WP-CLI command. The real work is making sure the existing site still works afterward – its URLs, plugins, database tables, rewrite rules, admin area, and any new subsites you create.
This tutorial walks through converting an existing WordPress single-site installation into a directory-based Multisite on a production server.
The final URL structure will look like this:
https://example.com/
https://example.com/site-one/
https://example.com/site-two/
It will not use subdomains such as:
https://site-one.example.com/
https://site-two.example.com/
I am using dummy server information throughout this guide. Replace the domain, username, paths, and database prefix with the values from your own server.
Example server setup used in this guide
| Domain | example.com |
| WordPress path | /home/example.com/public_html |
| Linux user | wpuser123 |
| Database prefix | wp_ |
| Network type | Directory-based |
| Test subsite | https://example.com/multisite-test/ |
1. Do not run WP-CLI as root unless you have a very good reason
If you are logged into the server as root and run:
wp core version
WP-CLI may warn that you are running WordPress as root.
You can bypass the warning with --allow-root, but I would not use that as the normal approach on a production WordPress server. Many WP-CLI commands load WordPress, which can also load plugin and theme code. Running that code as root gives it far more system access than it needs.
A better approach is to find the Linux user that owns the site and run WP-CLI as that user.
2. Find the WordPress installation
If the server hosts several websites, locate the WordPress configuration files first:
find /home -maxdepth 4 -type f -name wp-config.php -print
You may get a result such as:
/home/example.com/public_html/wp-config.php
That tells us the WordPress document root is probably:
/home/example.com/public_html
3. Find the Linux owner of the site
Check who owns wp-config.php:
stat -c 'Owner: %U | Group: %G | File: %n' \
/home/example.com/public_html/wp-config.php
For example:
Owner: wpuser123 | Group: wpuser123 | File: /home/example.com/public_html/wp-config.php
Also inspect the parent directory and document root:
ls -ld /home/example.com /home/example.com/public_html
Do not change ownership just because the groups look different. Hosting panels, LiteSpeed setups, and PHP handlers sometimes use different groups intentionally.
4. Make sure the Linux account is valid
getent passwd wpuser123
A normal result looks something like:
wpuser123:x:1013:1013:,,,:/home/example.com:/bin/bash
From this point on, run WordPress commands as that user:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
COMMAND
The -H flag makes sudo use the target user’s home directory instead of keeping root’s home environment.
5. Confirm that you are working on the right WordPress site
Check the WordPress version:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
core version
Then check the site’s home URL:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
option get home
And the WordPress installation URL:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
option get siteurl
On a normal root-domain installation, both will usually return:
https://example.com
If either command points somewhere unexpected, stop and find out why before touching Multisite.
6. Check whether Multisite is already enabled
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
core is-installed --network
echo $?
The shell exit code tells us what WordPress found:
0– Multisite is already installed.1– the installation is currently single-site.
If you get 0, do not run the conversion command again.
7. Check free disk space
df -h /home/example.com
This is easy to overlook. A database export and filesystem archive both need free space, and filling a production filesystem can cause much bigger problems than a failed Multisite conversion.
8. Check the database prefix
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
config get table_prefix
The result might be:
wp_
A custom prefix is fine. We only need to know it because new Multisite tables will use the same prefix.
9. Record the current permalink structure
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
option get permalink_structure
A common result is:
/%postname%/
Keep a note of this. After the conversion, we will test both the existing site’s URLs and pretty permalinks inside a subsite.
10. Check active plugins and the current theme
List active plugins:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
plugin list --status=active
Then check the active theme:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
theme list --status=active
Caching, security, authentication, redirect, and custom plugins deserve extra attention because they may behave differently on Multisite.
If you are developing your own plugin for the network, keeping plugin code out of functions.php becomes even more useful in a Multisite setup. I covered the basic structure in Your First Real Plugin: Headers, Structure and Not Using functions.php.
11. Check for must-use plugins
ls -la /home/example.com/public_html/wp-content/mu-plugins/ \
2>/dev/null || echo "No mu-plugins directory"
MU plugins cannot be disabled from the normal Plugins screen, so review them separately if the directory exists.
12. Check for existing Multisite constants
grep -nE \
'MULTISITE|SUBDOMAIN_INSTALL|DOMAIN_CURRENT_SITE|PATH_CURRENT_SITE|SITE_ID_CURRENT_SITE|BLOG_ID_CURRENT_SITE' \
/home/example.com/public_html/wp-config.php
On a normal single-site installation this may print nothing.
If Multisite constants are already present even though WordPress reports a single-site installation, investigate that mismatch before going further.
13. Optional – create a fresh database backup
You can skip this step if you already have a recent backup that you know can be restored. I would not skip it simply because a backup system is “probably” running somewhere.
Create a directory outside public_html:
mkdir -p /home/example.com/backups
chown wpuser123:wpuser123 /home/example.com/backups
chmod 750 /home/example.com/backups
Export the database:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
db export \
/home/example.com/backups/example-before-multisite.sql \
--add-drop-table
The --add-drop-table option is useful for a full rollback because the dump can remove existing tables before recreating them.
Check that the file exists:
ls -lh /home/example.com/backups/example-before-multisite.sql
14. Check the database before converting it
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
db check
The existing WordPress tables should report OK.
This step is worth doing even when you already have a backup. WordPress is about to create network tables and change the site’s database structure. It is better to know about an existing table problem before that happens.
15. Optional – back up the WordPress files
If you already have a verified filesystem backup, you can skip this part.
Otherwise:
cd /home/example.com
tar \
--exclude='./backups' \
-czf backups/example-files-before-multisite.tar.gz \
public_html
Set the archive owner:
chown wpuser123:wpuser123 \
/home/example.com/backups/example-files-before-multisite.tar.gz
Then test that the archive can actually be read:
tar -tzf \
/home/example.com/backups/example-files-before-multisite.tar.gz \
| head -20
16. Optional – create backup checksums
A checksum gives you a simple way to detect accidental corruption later.
sha256sum \
/home/example.com/backups/example-before-multisite.sql \
> /home/example.com/backups/example-before-multisite.sql.sha256
sha256sum \
/home/example.com/backups/example-files-before-multisite.tar.gz \
> /home/example.com/backups/example-files-before-multisite.tar.gz.sha256
17. Optional – save separate copies of wp-config.php and .htaccess
These are the two configuration files most likely to change during the conversion, so I like having small standalone copies even when a full backup already exists.
cp -a \
/home/example.com/public_html/wp-config.php \
/home/example.com/backups/wp-config.php.pre-multisite
cp -a \
/home/example.com/public_html/.htaccess \
/home/example.com/backups/.htaccess.pre-multisite
18. Check for root-level URL conflicts
This matters much more with a directory network than it does with a subdomain network.
Suppose your current website already has these pages:
https://example.com/news/
https://example.com/support/
https://example.com/contact-us/
You should not later try to create subsites using news, support, or contact-us as their slugs.
List the existing published page slugs:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
post list \
--post_type=page \
--post_status=publish \
--fields=ID,post_name,post_title \
--format=table
A directory-based network shares the root URL space with the main site, so slug planning matters.
19. Check the homepage configuration
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
option get show_on_front
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
option get page_on_front
You may not need these values during the conversion, but they are useful if the main site’s homepage behaves differently afterward.
20. Temporarily deactivate normal plugins
If the site can tolerate a short maintenance window, deactivate ordinary plugins before the conversion.
For one plugin:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
plugin deactivate example-plugin
Or deactivate all normal plugins:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
plugin deactivate --all
Check that nothing is still active:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
plugin list --status=active
Do not delete the plugins, and do not deactivate the active theme.
21. Convert the site to directory-based Multisite
Now we can perform the actual conversion:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
core multisite-convert \
--title="Example Network"
Notice that the command does not contain:
--subdomains
Leaving that option out gives us the directory-style network we want:
https://example.com/site-one/
https://example.com/site-two/
When the command succeeds, WP-CLI creates the Multisite database tables and adds the network constants to wp-config.php.
Once it reports success, do not run multisite-convert again.
22. Check the Multisite constants added to wp-config.php
grep -nE \
'MULTISITE|SUBDOMAIN_INSTALL|DOMAIN_CURRENT_SITE|PATH_CURRENT_SITE|SITE_ID_CURRENT_SITE|BLOG_ID_CURRENT_SITE' \
/home/example.com/public_html/wp-config.php
You should find values similar to:
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 );
The line that confirms the network type is:
define( 'SUBDOMAIN_INSTALL', false );
false means the network uses directories rather than subdomains.
23. Check that WordPress recognizes the network
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
core is-installed --network
echo $?
The expected exit code is:
0
Then list the sites:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
site list
At this point you will normally see only the original site:
https://example.com/
24. Confirm the network type from inside WordPress
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
eval '
echo "Multisite: ";
var_export( is_multisite() );
echo PHP_EOL;
echo "Subdomain install: ";
var_export( is_subdomain_install() );
echo PHP_EOL;
'
The result should be:
Multisite: true
Subdomain install: false
25. Replace the old single-site rewrite rules
The database conversion is only part of the job. A directory-based network also needs rewrite rules that understand subsite paths.
First inspect the current file:
cat /home/example.com/public_html/.htaccess
If it contains the normal WordPress single-site block, replace that WordPress section with the Multisite version:
# BEGIN WordPress Multisite
# Using subfolder network type.
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
These rules let the shared WordPress installation handle URLs such as:
/site-one/
/site-one/wp-admin/
/site-one/sample-page/
If your .htaccess also contains hosting-panel rules, security directives, redirects, PHP configuration, or LiteSpeed settings, do not wipe out the whole file. Replace only the WordPress rewrite section unless you know exactly what the other directives do.
Nginx does not use .htaccess, so an Nginx-only setup needs equivalent server-level rewrite rules instead.
26. Check .htaccess ownership and permissions
ls -l /home/example.com/public_html/.htaccess
A normal result might look like:
-rw-r--r-- 1 wpuser123 wpuser123 ... .htaccess
If editing the file as root changed its owner, restore it:
chown wpuser123:wpuser123 \
/home/example.com/public_html/.htaccess
Typical permissions are:
chmod 644 /home/example.com/public_html/.htaccess
27. Flush rewrite rules
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
rewrite flush
You want:
Success: Rewrite rules flushed.
You can also flush the WordPress object cache:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
cache flush
If your SSH connection drops after WP-CLI has already printed a success message, do not rerun the Multisite conversion just because the terminal disconnected. Reconnect and check the current state first.
If something does fail later, enable logging without showing PHP errors to visitors. The setup in WP_DEBUG_LOG Without Displaying Errors to Visitors is a much safer production debugging approach than turning on visible error output.
28. Test the existing site before creating a subsite
Check the homepage:
curl -I https://example.com/
A healthy result will normally include:
HTTP/2 200
Then test Network Admin:
curl -I https://example.com/wp-admin/network/
When you use curl without an authenticated WordPress session, a 302 redirect to wp-login.php is normal.
It means the route exists and WordPress is asking you to log in.
29. Create a temporary subsite
This is the test that tells us whether the network actually works, not just whether WordPress created some database tables.
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
site create \
--slug=multisite-test \
--title="Multisite Test"
A successful result should look similar to:
Success: Site 2 created: https://example.com/multisite-test/
List the network again:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
site list
You should now have something like:
1 https://example.com/
2 https://example.com/multisite-test/
30. Test the subsite frontend
curl -I https://example.com/multisite-test/
You want an HTTP 200.
If the main site works but the subsite returns 404, look at rewrite rules and web-server configuration first.
31. Test the subsite admin URL
curl -I https://example.com/multisite-test/wp-admin/
A 302 redirect to login is expected when the request is not authenticated.
The important part is that WordPress understands /multisite-test/wp-admin/ as the admin route for that subsite.
32. Check the subsite home and siteurl values
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
option get home \
--url=https://example.com/multisite-test/
Expected:
https://example.com/multisite-test
Then:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
option get siteurl \
--url=https://example.com/multisite-test/
It should return the same subsite URL.
33. Check the new subsite database tables
If the new site’s ID is 2 and your prefix is wp_, run:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
db tables --all-tables-with-prefix \
| grep '^wp_2_'
You should see tables such as:
wp_2_commentmeta
wp_2_comments
wp_2_links
wp_2_options
wp_2_postmeta
wp_2_posts
wp_2_term_relationships
wp_2_term_taxonomy
wp_2_termmeta
wp_2_terms
The main site keeps its original tables:
wp_posts
wp_postmeta
wp_options
wp_terms
User accounts are shared through network-level tables such as:
wp_users
wp_usermeta
This is an important detail when writing Multisite plugins. Code that assumes every table belongs to a single site can behave very differently once $wpdb->prefix changes with the current blog.
34. Check the Multisite network tables
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
db tables --all-tables-with-prefix \
| grep -E '^wp_(blogs|site|sitemeta)$'
You should find tables such as:
wp_blogs
wp_site
wp_sitemeta
35. Test a real pretty permalink inside the subsite
A working subsite homepage is good, but it does not prove that deeper routes work.
Create a temporary page:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
post create \
--url=https://example.com/multisite-test/ \
--post_type=page \
--post_title="Multisite Test Page" \
--post_name=multisite-test-page \
--post_status=publish
Then request the pretty permalink:
curl -I \
https://example.com/multisite-test/multisite-test-page/
If that returns HTTP 200, the full request path is working:
domain
→ subsite path
→ Multisite lookup
→ rewrite rules
→ subsite
→ page permalink
This kind of end-to-end test is much more useful than assuming the problem must be where an error first appears. That same debugging idea comes up in The Error Line Is Not Where the Bug Is.
36. Check Network Admin in the browser
Log into:
https://example.com/wp-admin/
You should now see:
My Sites
→ Network Admin
The direct Network Admin URL is:
https://example.com/wp-admin/network/
Open the Sites screen and make sure both the root site and test subsite appear.
37. Reactivate plugins one at a time
Do not immediately network-activate every plugin that was active before the conversion.
If a plugin belongs only on the main site, activate it there:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
plugin activate example-plugin \
--url=https://example.com/
Check its status:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
plugin list \
--url=https://example.com/
Then retest both sites:
curl -I https://example.com/
curl -I https://example.com/multisite-test/
Only network-activate a plugin when you actually want it running across every site:
wp plugin activate example-plugin --network
Site activation and network activation are different choices. Treat them that way.
38. Leave caching until the network is known to work
If you temporarily disabled a WordPress caching plugin, leave it off until the main site, Network Admin, subsite, and nested permalink tests all pass.
Your web server may still be LiteSpeed even when the LiteSpeed Cache WordPress plugin is inactive. Those are separate things.
If you also use a CDN, purge its cache after the Multisite routing is settled. Cached redirects or cached 404 responses can make a correct configuration look broken.
39. Optional – make a post-conversion database backup
Once everything works, a second database snapshot gives you a clean “Multisite working” restore point.
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
db export \
/home/example.com/backups/example-after-multisite.sql \
--add-drop-table
You then have two useful states:
example-before-multisite.sql
example-after-multisite.sql
40. Delete the temporary test subsite
First check the site IDs again:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
site list
If the test site is really site ID 2, remove it:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
site delete 2 --yes
Always verify the ID before using site delete.
The original production site is normally blog ID 1. Do not blindly copy a site-delete command from a tutorial and assume the numbers match your network.
41. Check whether the temporary tables are gone
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
db tables --all-tables-with-prefix \
| grep '^wp_2_'
If WordPress removed the site cleanly, this should normally return nothing.
You can also check for a leftover uploads directory:
ls -ld \
/home/example.com/public_html/wp-content/uploads/sites/2 \
2>/dev/null \
|| echo "Site 2 uploads directory does not exist"
Do not start manually dropping tables simply because you see something you do not recognize. Confirm that it is truly orphaned first.
42. Run one final Multisite check
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
eval '
echo "Multisite: " .
( is_multisite() ? "yes" : "no" ) .
PHP_EOL;
echo "Network type: " .
( is_subdomain_install() ? "subdomain" : "subdirectory" ) .
PHP_EOL;
echo "Main site: " .
network_home_url() .
PHP_EOL;
'
You want:
Multisite: yes
Network type: subdirectory
Main site: https://example.com/
And finally:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
core is-installed --network
echo $?
The expected exit code is 0.
43. Check a few real production URLs
curl -I https://example.com/
curl -I https://example.com/about-us/
curl -I https://example.com/contact-us/
curl -I https://example.com/services/
These should return HTTP 200 unless you intentionally redirect one of them.
Also check:
curl -I https://example.com/wp-admin/network/
A 302 to the login page is normal for an unauthenticated request.
44. Create real subsites when you are ready
You can now add sites from:
My Sites
→ Network Admin
→ Sites
→ Add New
Or use WP-CLI:
sudo -u wpuser123 -H wp \
--path=/home/example.com/public_html \
site create \
--slug=company-a \
--title="Company A"
That creates:
https://example.com/company-a/
A few security details worth keeping
Multisite gives the Super Admin much more control than an ordinary site administrator, so protect that account carefully. Use a strong unique password and multi-factor authentication when possible.
Keep using the site’s Linux user for WP-CLI instead of root:
sudo -u wpuser123 -H wp ...
Keep database and filesystem backups outside the public web directory. This is safer:
/home/example.com/backups/
than placing SQL dumps somewhere under:
/home/example.com/public_html/
And do not network-activate a plugin simply because Multisite gives you the button. First decide whether every site really needs it.
What changes in the database after Multisite is enabled?
WordPress does not create a completely separate database for every subsite.
The original site continues using tables such as:
wp_posts
wp_postmeta
wp_options
wp_terms
A second site gets numbered tables:
wp_2_posts
wp_2_postmeta
wp_2_options
wp_2_terms
A third site would use:
wp_3_posts
wp_3_postmeta
wp_3_options
wp_3_terms
User accounts remain shared through tables such as wp_users and wp_usermeta, while network information is kept in tables including wp_blogs, wp_site, and wp_sitemeta.
This matters when writing custom code. A plugin that stores site-specific data should understand the current blog context instead of assuming the root site’s prefix is always correct.
Do not ignore the SEO side of the conversion
Turning on Multisite does not automatically hurt SEO. Changing existing URLs without noticing can.
After the conversion, make sure important root-site URLs still resolve exactly as they did before:
https://example.com/about-us/
https://example.com/contact-us/
https://example.com/services/
Also check canonical URLs, XML sitemaps, robots directives, structured data, Open Graph URLs, pagination, media URLs, and redirects.
Most importantly, do not create a subsite whose slug already belongs to indexed content on the main site.
How do you know the conversion is really finished?
I would consider the job complete only after all of these checks pass:
- The original homepage returns HTTP
200. - Existing pages and posts still work.
is_multisite()returnstrue.is_subdomain_install()returnsfalse.wp core is-installed --networkexits with0.- Network Admin opens correctly.
- A temporary directory-based subsite can be created.
- The subsite homepage returns HTTP
200. - The subsite admin route works.
- The subsite has its own numbered database tables.
- A pretty permalink inside the subsite returns HTTP
200. - Required plugins can be reactivated without breaking the main site or subsite.
- The temporary site can be removed cleanly.
- No important production URL changed unexpectedly.
Final thoughts
The command that converts WordPress to Multisite is the easy part:
wp core multisite-convert
The part that deserves attention is everything around it.
Check who owns the site. Check the database before changing it. Know which plugins are active. Record the existing URLs. Make sure the rewrite rules match a directory network. Create a real subsite. Test its admin area. Create a page inside it. Check the new database tables. Then reactivate plugins one by one.
When those pieces all work together, you have more than a Multisite flag in wp-config.php. You have a directory-based WordPress network that has actually been tested on the production stack.

