Last week I went into detail about how we’ve simplified our procedure for keeping WordPress up to date by using Composer for dependency management. Stage two in our process to clean up our many separate installations of WordPress is to create a single “network” where all the sites on our server share the same core code and plugins. Why update 19 installations when you can just update one?
Before you can get started with this you need a functioning, single site installation of WordPress with permalinks already configured, which you can convert into the master site of the new multisite network. It looks like established practice is for the master site to be a fully functioning WordPress site in its own right, but I wanted to do things a little differently by hosting the network at a subdomain of my main domain and then creating the new sites as subdomains of that domain. That way, we can point our clients at (something similar to) their-website-name.websites.praterraines.co.uk so they can make changes and sign off before go live. We won’t be allowing users to create their own blogs, so there’s no real value to us in websites.praterraines.co.uk being a working site beyond giving us access to the network admin panel.
Before we can run the multisite creation tool in the admin area we need to edit wp-config.php
to allow the installation.
define('WP_DEBUG', false); /* Multisite */ define('WP_ALLOW_MULTISITE', true);
You can then return to the admin panel and use the tool to create a subdomain based multisite installation which will provide a new .htaccess
and some more lines for the wp-config.php
. I’ve also added some cookie related config because in my experience there are sometimes issues with logins if you don’t.
/* Multisite */ define('WP_ALLOW_MULTISITE', true); define('MULTISITE', true); define('SUBDOMAIN_INSTALL', true); define('ADMIN_COOKIE_PATH', '/'); define('COOKIE_DOMAIN', ''); define('COOKIEPATH', ''); define('SITECOOKIEPATH', ''); define('DOMAIN_CURRENT_SITE', 'websites.praterraines.co.uk'); define('PATH_CURRENT_SITE', '/'); define('SITE_ID_CURRENT_SITE', 1); define('BLOG_ID_CURRENT_SITE', 1);
We’ve got a sunrise.php
which is executed on every request, which redirects any request for the master site frontend to the admin area, sets some site-specific config options on particular sites, and (by hooking in to the ms_site_not_found
action) outputs a branded placeholder content with a 404 status if a user requests a site which can’t be found. This last trick allows us to point future domain names at the WordPress virtualhost before we put them live. Customers will see a placeholder until we’re ready, and then we can put the site live at any time simply by changing the URL in the WordPress network admin area.
define('BLOG_ID_CURRENT_SITE', 1); // Include our sunrise.php which outputs a site not found page for deactivated or not present hosts. define('SUNRISE', true); // Just in case the passthrough doesn't work. define('NOBLOGREDIRECT', 'https://www.praterraines.co.uk/site-placeholder/');
Here’s a snippet from our wp-content/sunrise.php
:
function praterraines_hosting_site_not_found() { status_header(404); // Output the branded placeholder. // ... exit; } /** * If the site cannot be found, output our branded placeholder content and exit. */ add_action("ms_site_not_found", "praterraines_hosting_site_not_found"); /** * The main network site doesn't display any content. It simply redirects to the admin page. */ add_filter("pre_get_site_by_path", function($site, $domain, $path, $segments, $paths){ if ($domain == DOMAIN_CURRENT_SITE) { if (!preg_match("[/wp-(admin|login)]", $_SERVER["REQUEST_URI"])) { header("Location: /wordpress/wp-admin/", true, 301); exit; } } else { // ... } }, 10, 5);
We do the same thing with deleted blogs by calling praterraines_hosting_site_not_found
from a wp-content/blog-deleted.php
as well.
Some of the plugins we use on every site allow configuration in wp-config.php
to save you having to enter your mailserver details or Akismet anti-spam key on every new website. You’ll need to replace the placeholders with your specific values.
// SMTP settings define('WPMS_ON', true); define('WPMS_MAIL_FROM', 'info@praterraines.co.uk'); define('WPMS_MAIL_FROM_NAME', 'Prater Raines'); define('WPMS_MAILER', 'smtp'); // Possible values 'smtp', 'mail', or 'sendmail' define('WPMS_SET_RETURN_PATH', 'false'); // Sets $phpmailer->Sender if true define('WPMS_SMTP_HOST', $smtp_host); // The SMTP mail host define('WPMS_SMTP_PORT', $smtp_port); // The SMTP server port number define('WPMS_SSL', ''); // Possible values '', 'ssl', 'tls' - note TLS is not STARTTLS define('WPMS_SMTP_AUTH', true); // True turns on SMTP authentication, false turns it off define('WPMS_SMTP_USER', $smtp_username); // SMTP authentication username, only used if WPMS_SMTP_AUTH is true define('WPMS_SMTP_PASS', $smtp_password); // SMTP authentication password, only used if WPMS_SMTP_AUTH is true // Akismet (WordPress.com) API key define('WPCOM_API_KEY', $wordpress_api_key);
Now it’s just a question of adding all the existing sites to the new multisite installation and porting the content across. More on that later.