RE:NODE

WordPress Multisite hosting: subdomain vs subdirectory

By RE:NODE Team
18 min read


WordPress Multisite lets one WordPress installation run many sites: one set of core files, one plugin and theme directory, one database, one PHP process pool, and a Network Admin screen above all of it. It is the right tool when the sites belong to the same owner, look alike and are managed together - a university's department sites, a franchise, a set of language editions, a shop with regional storefronts. It is the wrong tool when the sites are unrelated, belong to different clients, or need different plugins and different risk. The choice that cannot be undone cheaply is subdirectory (example.com/shop/) or subdomain (shop.example.com); the rest of this post explains how to make it, what it asks of your hosting, and where Multisite quietly makes life harder than a handful of separate installs.

What Multisite actually is#

A single-site WordPress install has one set of tables in the database: wp_posts, wp_options, wp_comments and so on. Multisite keeps the same layout for the first site and adds a numbered copy of the per-site tables for every site you create: wp_2_posts, wp_2_options, wp_3_posts and so on. Each site gets roughly a dozen of its own tables. A few tables are shared by the whole network:

  • wp_users and wp_usermeta - one account list for the whole network. A person has one login, and is given a role on each site separately.
  • wp_blogs and wp_site - the registry of sites and of networks.
  • wp_sitemeta - network-wide settings, including the list of super admins and which plugins are network-activated.
  • wp_signups and wp_registration_log - used when people may register their own sites.

Files follow the same split. Code is shared: there is one wp-content/plugins, one wp-content/themes, one copy of core. Uploads are separate, and each site after the first writes to wp-content/uploads/sites/<id>/. Settings, content, menus, widgets and the active theme are per site. Which plugins are installed is a network decision; which of them are active is a per-site decision, unless the super admin has network-activated them.

That sharing is the whole appeal. You update WordPress once, a security patch for a plugin lands on every site at the same moment, and a new site is a form rather than a new server. It is also the whole risk, which is covered further down.

Terminology trips people up. A network is the whole installation. A site is one member of it. The super admin is a network-level role that can install plugins and themes, create and delete sites, and edit any user. An ordinary administrator of a site cannot install plugins or themes on a multisite network at all, only switch on the ones the super admin has made available. Clients who expect to install their own plugins are often unhappy with this.

Subdomain or subdirectory#

You choose this once, during network setup, and WordPress will not let you change it afterwards from the interface. Converting later means editing wp-config.php, rewriting rules and URLs in every site's tables, and a lot of testing, so decide first.

SubdirectorySubdomain
Site URLsexample.com/shop/shop.example.com
DNS neededOne A recordA record per name, or a wildcard
Certificate neededOne nameOne name per site, or a wildcard
Reads asOne site with sectionsSeparate sites

The practical differences are these.

DNS and certificates. A subdirectory network needs nothing beyond the DNS record you already have for the main domain, and one certificate covers every site. A subdomain network needs every site's hostname to resolve to the server, and every hostname to be covered by a certificate. The usual answer is a wildcard DNS record, *.example.com, pointing at the same address as the apex, and a wildcard certificate or a certificate per name. Wildcard certificates are normally issued through DNS validation, which depends on your DNS provider having an API your tooling can call. See DNS records explained for what a wildcard record does and does not match, and subdomains for servers for the general idea of one name per service.

Search engines and identity. Search engines treat subdomains as separate sites to a degree and subdirectories as part of the parent. If the sites are sections of one brand and you want them to share ranking, subdirectories are the better fit. If they are separate properties that merely share a parent name, subdomains are the honest URL.

Path conflicts. On a subdirectory network, the first path segment belongs to Multisite. A site named blog takes /blog/, so a page, category or custom post type slug on the main site that is also blog collides with it. WordPress refuses some reserved names (wp-admin, files, page, feed and similar) when you create a site, but it cannot know about every slug you have used in the main site's content. Subdomains avoid this entirely.

Age of the install. WordPress only offers the subdirectory option on a fresh install, or one that is less than a month old, because on an established site existing permalinks might collide with new site paths. Subdomain networks have no such restriction. If you are converting a long-lived site and want subdirectories, expect to check every top-level slug first.

Ports and odd addresses. Multisite does not work reliably when the site address includes a non-standard port, and the network setup screen will say so. Run it on the standard ports behind a proxy.

A rule of thumb that holds up: if you can run a wildcard record and a certificate for it, and the sites are separate in the visitor's mind, use subdomains; if the sites are sections of one thing, or you are limited to a single DNS record, use subdirectories. And whichever you choose, every site can later be given its own domain name, as described under domain mapping.

Turning Multisite on#

Start from a working single site, with a fresh backup. Install WordPress manually covers getting to that point if you are doing it from scratch.

  1. Add one line to wp-config.php, above the comment that says to stop editing:
wp-config.php
define( 'WP_ALLOW_MULTISITE', true );
  1. In the dashboard, deactivate all plugins (plugins that touch rewrites or redirects interfere with the next step; reactivate them afterwards).
  2. Open Tools, then Network Setup. WordPress shows the choice of subdomains or subdirectories, a network title and the super admin email.
  3. Click Install. WordPress does not write the changes itself. It shows two blocks of code to paste: constants for wp-config.php and rewrite rules for .htaccess or the web server.

For a subdomain network, the wp-config.php block looks like this, placed immediately after the WP_ALLOW_MULTISITE line:

wp-config.php
define( 'MULTISITE', true );define( 'SUBDOMAIN_INSTALL', true );define( 'DOMAIN_CURRENT_SITE', 'example.com' );define( 'PATH_CURRENT_SITE', '/' );define( 'SITE_ID_CURRENT_SITE', 1 );define( 'BLOG_ID_CURRENT_SITE', 1 );

For a subdirectory network the only difference is SUBDOMAIN_INSTALL, which is false. That one constant is what the whole subdomain-or-subdirectory decision reduces to in code, which also tells you why changing it later is possible but never a one-line job: every existing URL in the database was written for the old layout.

Then replace WordPress's default rewrite rules. On Apache, in .htaccess, a subdirectory network uses:

.htaccess
RewriteEngine OnRewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]RewriteBase /RewriteRule ^index\.php$ - [L]# add a trailing slash to /wp-adminRewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]RewriteCond %{REQUEST_FILENAME} -f [OR]RewriteCond %{REQUEST_FILENAME} -dRewriteRule ^ - [L]RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]RewriteRule . index.php [L]

A subdomain network drops the optional site-name prefix from those rules:

.htaccess
RewriteEngine OnRewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]RewriteBase /RewriteRule ^index\.php$ - [L]RewriteCond %{REQUEST_FILENAME} -f [OR]RewriteCond %{REQUEST_FILENAME} -dRewriteRule ^ - [L]RewriteRule ^(wp-(content|admin|includes).*) $1 [L]RewriteRule ^(.*\.php)$ $1 [L]RewriteRule . index.php [L]

On nginx there is no .htaccess. A subdomain network needs nothing special beyond try_files $uri $uri/ /index.php?$args; and a server_name that matches the wildcard, while a subdirectory network adds a block to strip the site prefix from admin and PHP paths:

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

Which web server your host runs, and whether it reads .htaccess at all, is the first thing to find out before you start - the rewrite step is where most failed Multisite setups die, with every subsite returning the main site's 404 page or its admin redirecting in a loop.

  1. After saving both files, log in again (the cookies change), then reactivate your plugins from Network Admin.

If you would rather skip the interface, WP-CLI does the whole conversion in one command, run from the site's directory:

bash
$ wp core multisite-convert --title="Example Network" --subdomains$ wp site list

Drop --subdomains for a subdirectory network. The command writes the constants for you and creates the network tables; the rewrite rules still need to be in place.

Wildcard DNS for subdomain networks#

For shop.example.com to load, the name has to resolve to the server. You have two options. Create an A record for each new site by hand, or create one wildcard record once:

code
example.com.      3600  IN  A  203.0.113.10*.example.com.    3600  IN  A  203.0.113.10

The wildcard matches any name one label deep that has no record of its own, so shop.example.com and anything.example.com both resolve, while a.b.example.com does not. An explicit record for mail.example.com or www.example.com always wins over the wildcard, which means you can keep email and other services on their own addresses.

The server also has to answer for those names. WordPress itself routes by the Host header: when a request for shop.example.com arrives, it looks the hostname up in wp_blogs and loads that site. So the web server must send every one of those hostnames to the same WordPress directory - a default virtual host, or a server_name *.example.com entry. Test it before creating real sites:

bash
$ dig +short shop.example.com203.0.113.10$ curl -sI https://shop.example.com/ | head -n 1

If the first command prints nothing, the wildcard record is missing or has not propagated. If it prints the address but the second shows a certificate warning, DNS is fine and the certificate does not cover the name.

On RE:NODE, app and web plans include a reverse-proxy slot: you point an A record at the address shown, and the certificate is issued and renewed automatically. That describes a certificate for the names you attach there. We do not state that wildcard names or wildcard certificates are handled, and we do not publish how many proxy slots each plan has, so if you plan a subdomain network with many hostnames, ask support before you build it. A subdirectory network sidesteps the question, because only one name is involved. Your domain and its certificate explains how the certificate part works.

Creating sites, users and domain mapping#

With the network running, new sites come from Network Admin, then Sites, then Add New, or from WP-CLI:

bash
$ wp site create --slug=shop --title="Shop" --email=admin@example.com$ wp site list --fields=blog_id,url,registered$ wp super-admin add alice$ wp plugin activate wordpress-seo --network$ wp theme enable twentytwentyfive --network

--slug becomes the subdomain or the subdirectory, depending on the network type. wp theme enable --network makes a theme available to every site, while a plain wp theme enable <theme> --url=shop.example.com makes it available to just one.

Under Network Admin, then Settings, are the options that matter most:

  • Registration: disabled, user accounts only, logged-in users may register new sites, or both. Leave it disabled unless you are running a public service, because open site registration attracts spam sites quickly.
  • Upload file types and size: the network-wide Max upload file size is set here (the field is fileupload_maxk, and the default is small, 1500 KB), separately from PHP's own upload_max_filesize. Both limits apply, and the lower wins. See php.ini settings that matter.
  • Menu settings: whether site admins see the Plugins menu at all.

A constant worth setting early controls where visitors go when they request a site that does not exist:

wp-config.php
define( 'NOBLOGREDIRECT', 'https://example.com' );

Without it, a mistyped subdomain shows a generic "this site does not exist" page.

Domain mapping means giving a site its own domain, www.shop-example.net, instead of a name under the network's domain. Since WordPress 4.5 this is built in and needs no plugin: in Network Admin, edit the site and change its Site Address (URL) to the new domain. The rest is outside WordPress:

  1. Point the new domain's A record at the same server.
  2. Make sure the web server answers for that hostname and that a certificate covers it.
  3. Change the Site Address under Sites, Edit, in Network Admin.
  4. Run a search and replace on the site's own tables, since content still contains the old URLs:
bash
$ wp search-replace 'shop.example.com' 'www.shop-example.net' \    --url=shop.example.com --network --dry-run

Run it with --dry-run first, read the counts, then run it again without. In a subdomain network a mapped domain also means a separate login cookie, so users logged in on the network domain are not logged in there. Some setups also add define( 'COOKIE_DOMAIN', '' ); to stop WordPress forcing the cookie onto the parent domain.

What Multisite costs in resources#

Multisite does not use fewer resources per site in any dramatic way. What it saves is duplicate disk for core, plugin and theme files, and the work of maintaining many installs. What it shares is everything else, and shared means contended.

PHP processes and memory. One site or a hundred, WordPress loads on each request with that site's active plugins and theme. Memory per request depends on what is active on the site being served, not on the size of the network. But all the sites draw from the same pool of PHP workers and the same memory limit. A single busy site, or a slow plugin on one site, can take the workers that all the others need. How much RAM does WordPress need gives per-site figures; for a network, add up the busy sites rather than counting all of them, and leave headroom for the busiest to spike.

The database. Each site adds around a dozen tables, so a network of 200 sites holds well over two thousand. That is fine for MySQL, but tools and plugins that scan every table, backup plugins, search-and-replace across the network and some migration scripts all slow down with it. WordPress itself has a safeguard for large networks: once a network passes 10,000 sites or users (the wp_is_large_network threshold, filterable) it skips some network-wide counting queries that would otherwise be too slow. You are unlikely to meet that limit on a small plan, but you will meet the effect of ten thousand wp_options autoload rows long before that. Each site loads its own autoloaded options on every request, so a plugin that bloats wp_options on one site slows that site only, but the shared wp_users and wp_usermeta tables grow with the whole network and are hit on every login.

Object cache. A persistent object cache helps Multisite more than a single site, because every request does at least a few lookups against the shared network tables. The cache keys include the site ID, so a good object cache plugin keeps the sites apart. Valkey object cache for WordPress walks through setting it up, and WordPress speed and caching covers page caching, which on a network has to be network-aware or it serves one site's pages for another's hostname.

WP-Cron. Every site has its own schedule of jobs, kept in its own wp_<id>_options table, and WP-Cron fires from page views on each site separately. A quiet site in a network of fifty can go days without its scheduled posts publishing. The real-cron fix from WP-Cron vs real cron still applies, with one change: you have to run the due jobs for every site, not just one. With DISABLE_WP_CRON set, a single cron line can do that:

bash
$ wp site list --field=url | xargs -I % wp --url=% cron event run --due-now

Run that every five minutes. Each site is a fresh WordPress boot, so on a network of fifty sites, expect it to take a while and to spike CPU for the duration. Stagger it or split the list if your plan's CPU share is small. The Schedules tab in the RE:NODE panel is where a cron-style task is set up on the plans that have one.

Disk and uploads. Upload folders grow per site and are not shared, so one site with a large media library can fill the disk for all of them. Network Admin lets you set a per-site upload space allowance (blog_upload_space, default 100 MB), which is worth turning on if you hand sites to other people.

As a sizing note, a small network of a handful of lightweight sites will run on a plan that would host one moderate single site. A network of dozens of busy sites, or any site running WooCommerce, belongs on the larger plan sizes, or on a dedicated server when the load justifies it.

When not to use Multisite#

Multisite is attractive because it looks like a short cut to managing many sites. It is a trade, and for a lot of use cases it is the wrong one.

The sites belong to different people. The network's shared plugin directory, shared user table and shared super admin role mean that whoever controls the network controls every site. A client who needs to install their own plugin cannot. A client who needs a staging copy, a different PHP version, or a restore of just their site, will find that none of those is simple.

One site is much riskier than the others. A vulnerable plugin that is installed on the network is installed everywhere, even if only one site uses it, and a compromised super admin account is a compromised network. One infected site can reach the shared database and the shared code directory. Hardening matters more here; start with WordPress security hardening.

You need per-site restore. A backup of a Multisite network is a backup of one database and one file tree. Restoring a single site to yesterday means exporting its tables and its uploads/sites/<id>/ folder by hand. WP-CLI makes the export manageable:

bash
$ wp db export shop-site.sql --tables=$(wp db tables --scope=blog --url=shop.example.com --format=csv)

Importing that into a live network is where mistakes happen. If your sites need to be restorable independently, separate installs make that a button instead of an operation.

The sites need different software. Same WordPress version, same plugin versions, same PHP version for all. If one site needs a legacy plugin that breaks on current PHP, it holds back the rest. If one needs a plugin that is incompatible with Multisite - many are, particularly those that write their own tables or assume a single wp_options - it cannot be used at all. Check each plugin's documentation for Multisite support before committing.

The sites have uneven traffic. One launch, one viral post or one bot crawling a single site's tag archives slows everyone, because they share workers, database connections and memory. If you would have to limit one site to protect the rest, those sites should not share a process pool.

You only wanted tidy admin. If the real aim is to update many sites in one place, tools like WP-CLI over SSH, or a management dashboard, give that without sharing anything. Separate installs updated by a script are less elegant than Multisite and far easier to isolate.

Multisite fits well when the sites are siblings: the same owner, the same theme family, the same plugin set, comparable traffic, and a single person responsible for all of them. Outside that, separate installs are the safer default.

On RE:NODE the separate-install route is cheap to try. The WordPress line installs WordPress with wp-config.php already written for a MySQL database of its own and starts from $5 a month, and each plan has its own backup slots and its own panel. Several small plans give you per-site restore, per-site CPU and memory limits and no shared blast radius, at the price of updating each one. Migrating WordPress to a new host covers moving a site between installs, which also applies if you later split a network apart.

Moving a site in or out of a network#

Moving sites is where Multisite stops being tidy. An existing standalone site can be imported with the built-in Tools, Export and Import, which carries posts, pages and media references, or with a plugin that moves tables. Because table prefixes change to wp_<id>_, a straight database copy does not work; you rename the tables, update the wp_<id>_options row that stores the user roles (wp_<id>_user_roles) and fix the URLs.

Going the other way, a site leaves a network by exporting its tables, renaming them back to the default prefix, copying its uploads/sites/<id>/ folder to wp-content/uploads/, and running a search and replace on the paths and URLs. Users need to be recreated, since they live in the shared wp_users table. Plan this before you need it: it is the real cost of choosing Multisite for sites that might grow independent later.

Troubleshooting#

Every subsite shows the main site, or a 404. The rewrite rules are the old single-site ones, or the web server is not sending the hostname to this WordPress directory. Re-apply the block from Network Setup for your type, and confirm the virtual host answers for the wildcard.

Redirect loop on `/wp-admin/`. Usually cookies. Confirm DOMAIN_CURRENT_SITE matches the host in the address bar exactly (with or without www), clear cookies for the domain, and check that define( 'COOKIE_DOMAIN', '' ); is not set to something that does not match.

"Error establishing a database connection" on one subsite only. The site's tables are missing or the prefix is wrong - usually after a hand-made restore. Check wp db tables --url=shop.example.com lists the wp_<id>_ tables. For a network that is down entirely, WordPress white screen and 500 errors is the place to start.

Plugins vanish after the network is set up. Plugins were deactivated for the setup step, as intended. Reactivate them under Network Admin, then Plugins, and decide for each one whether it should be network activated.

New subdomain sites get a certificate warning. DNS works but the certificate covers only the apex. Either extend the certificate to the wildcard, or add each hostname to it before creating the site.

FAQ#

Can I convert a single WordPress site to Multisite later?

Yes. Add WP_ALLOW_MULTISITE, run Tools, Network Setup (or wp core multisite-convert) and paste the generated constants and rewrite rules. Take a backup first. Subdirectory networks are only offered on installs that are under a month old, so an older site converts to a subdomain network unless you fix that by hand.

Can I switch between subdomain and subdirectory afterwards?

Not from the interface. It means changing SUBDOMAIN_INSTALL, the rewrite rules and every stored URL on every site. On a network with real content it is a project, so treat the choice as permanent.

Does Multisite make WordPress faster?

No. A request to one site costs about what it costs on a standalone install, plus a few lookups in the network tables. What improves is maintenance effort, not speed, and heavy use of one site slows the others.

Can each site have its own domain name?

Yes, with built-in domain mapping since WordPress 4.5. Point the new domain at the server, make sure the web server and certificate cover it, then change the Site Address under Sites in Network Admin and run a search and replace.

How many sites can a network hold?

WordPress has no fixed limit in the software. In practice the limits are the database (about a dozen tables per site), the shared PHP workers and memory, and how long network-wide jobs such as backups and cron take. Past a few hundred active sites, ask whether it needs its own database server and tuned caching.

Do plugins work on Multisite?

Most do, but plugins that create their own tables, store settings in wp_options with hard-coded names, or assume one set of uploads can misbehave. Look for explicit Multisite support in the plugin's documentation and test on a staging network first.

Was this helpful?