Multisite: differences between subdomain and subdirectory mode? Can it be switch after installation?

From a user perspective there are two noticeable differences. First of all the GUI for creating new sites. Either it shows an input field for the first segment of the URL path (subdirectory) or an input field for the subdomain you want to use for the new site. (Setting the URL to a completely arbitrary value is only possible when editing an already created site. But it is possible in both modes.)

The other one is not evident on the first sight and and it only affects the subdirectory mode: the permalink structure of the main sites will have a static prefix blog/ that is not meant to be changed. (Even though there might be solutions do change or remove the prefix).

From a technical perspective it is a different system configuration that is defined by configuration constants, configuration values in database and also configuration of the web server. What defines the mode in first place it the constant IS_SUBDOMAIN_INSTALL which can be set to true (subdomain mode) or false (subdirectory mode). But that’s not the only place this information is stored. During the install process WordPress stores an integer value as site meta using the key subdomain_install. It can be read (or updated) via WP-CLI:

$ wp site option get subdomain_install
1

Subdirectory mode results further in an additional element on the list of forbidden site slugs: blog. These list is stored in the site meta illegal_names. Thus, in subdirectory mode it looks like this by default:

$ wp site option get illegal_names
array (
      0 => 'www',
      1 => 'web',
      2 => 'root',
      3 => 'admin',
      4 => 'main',
      5 => 'invite',
      6 => 'administrator',
      7 => 'files',
      8 => 'blog',
)

Apache rewrite configuration

As the web server is not responsible for WordPress internals, its configuration is all about routing the requested URL to the correct script or asset file. Primarily it’s about the question whether to route request URLs like /site-slug/wp-[admin|content|includes] and /site-slug/wp-*.php to the appropriate directories (/wp-*/) or scripts.

So the default suggested set of rewrite rules for subdirectory mode is this:

RewriteEngine On
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]

Whereas the set for subdomain mode is suggested as this:

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^wp-admin$ wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
RewriteRule ^(.*\.php)$ $1 [L]
RewriteRule . index.php [L]

What type of rule set is used highly depends on the desired domain schema you’re planing for your multisite. The more flexible one of course is the first one (sub directory) as the URL path segment («sub directory») is totally optional.

Sunrising (WordPress routing) and is_subdomain_install() API function

In order to know what mode is configured, WordPress provides the function is_subdomain_install() it returns true if:

  • the constant SUBDOMAIN_INSTALL is defined and set to true or
  • the constant SUBDOMAIN_INSTALL is not defined but VHOST is defined and set to 'yes'

It returns false if SUBDOMAIN_INSTALL is defined and set to false. And it returns anything else if SUBDOMAIN_INSTALL is defined but not of type boolean. The site meta value mentioned above is only used during the mutliste setup process. WordPress core relies only on the value of this constant everywhere else.

However, the value of is_subdomain_install() affects the routing of the requested URL to a specific site in the network only if the constants DOMAIN_CURRENT_SITE and PATH_CURRENT_SITE are not defined. (See ms_load_current_site_and_network()). That means it’s possible to use any domain as site URL in both modes (domain mapping).

Switching modes after setup

Switching form subdomain to subdirectory mode

  • Change the value of the constant IS_SUBDOMAIN_INSTALL from true to false
  • Set the value of site meta subdomain_install to 0:

    $wp site option set subdomain_install 0

  • Add the prefix /blog/ to the permalink structure of the main site. It will be fixed after the setting was updated.
  • Add blog to the list of forbidden site slugs. As this list is a serialized value, this should be done via a small PHP script that can be run via WP-CLI:

$ wp eval-file patch-forbidden-slugs.php

// patch-forbidden-slugs.php
<?php
$illegalNames = get_metadata( 'site', SITE_ID_CURRENT_SITE, 'illegal_names', true );
$blogSlug = 'blog';
if ( in_array( $blogSlug, $illegalNames ) ) {
    exit( 'Noting to do' . PHP_EOL );
}
$illegalNames[] = $blogSlug;
update_metadata( 'site', SITE_ID_CURRENT_SITE, 'illegal_names', $illegalNames );
exit( 'Done' . PHP_EOL );

Switching form subdirectory to subdomain mode

  • Change the value of the constant IS_SUBDOMAIN_INSTALL from false to true
  • Set the value of site meta subdomain_install to 1:

    $ wp site option set subdomain_install 1

  • Remove the /blog/ prefix from the permalink structure of the main site

  • Remove the blog slug from the list of forbidden names (adapt the script from above accordingly)

Conclusion

My remarks only consider a clean WordPress installation and an immediate following network installation. Also the latest WordPress version 4.8 is considered. It might be possible that there are implications when doing a multisite installation on an existing system that uses plugins or any older WordPress version.

However, my preferred set up is the subdomain mode with the .htaccess rule set for subdirectories as it offers the most flexibility out of the box. It even works with modern composer-controlled setups like WPStarter (with slight adaption of the .htaccess rules).

Leave a Comment