Migrating to a WordPress Network

I use this website to host a bunch of (mostly unrelated) services: wikis, my feed reader, and a couple of blogs for family members I like to keep separate. Those blogs used to each have their own WordPress install, which was not only a pain to keep up to date, but it also finally ate up all the SQL databases and subdomains I was allocated as part as my hosting plan. Setting up my wife’s new portfolio was an excellent excuse to find a better solution than to fire up yet another CMS instance. I decided to migrate the whole mess to a WordPress Network (previously called WordPress Multi-User). Which turned out to be much easier than I thought. Here’s how I did it and what I learned on the way.

1. Start fresh

I started with an (almost) fresh install of WordPress, the one that was powering this blog since November. Since I had used Softaculous to install it, I was able to setup automatic backups and updates while I was at it. I decided to move it to a subdirectory first to clean things up a bit on my home directory. According to the documentation, this would prevent me from creating subdomain sites (e.g. things like this.timtom.ch1 and that.timtom.ch) but I was able to find a way around this limitation by using the WP subdomain plugin, more on that later.

After moving WordPress to a new subdirectory, I checked that everything was still working on the main site. Since I already had a few posts live on that WordPress install, I backed everything up for good measure before I started the process.

2. Enable the Network feature

This is as easy as adding a single line to wp-config.php, and clicking through a few options on the admin interface. Since I was now running WordPress in its own directory, I knew running my Network under the subdomain model (this.timtom.ch) would not be straightforward, so chose to run it under the subdirectory model instead (timtom.ch/that).

Once my embryonic network was set up, I verified that the main site (this blog) was still working fine.

3. Import the other blogs into the new network

For each of the standalone WP instances I wanted to replace, I exported all content using the Tools > Export function.

Back in my Network admin interface, I then created a new site for each of them. I didn’t worry too much about naming the new sites, knowing I would fix their addresses later on. I chose unique names that I knew would not be conflicting with any pages I’d like to create on this blog in the future. I named them something like timtom.ch/sub-familynews etc.

Before importing the WXR files I had exported out of the old sites, I needed to install the WordPress Importer plugin. Despite being an official WP plugin, it unfortunately has a pretty bad reputation, and justifiably so, because of its poor interface and error management. It basically gives no feedback during the import process, which is unnerving and problematic if anything goes wrong. Fortunately, nothing bad happened to me. I imported each blog into the new sites I had just created, making sure to reallocate posts to the users that already had an account on my network, or to allow WordPress to create new accounts for those that didn’t. I chose to import all media, which is important since it will make a new local copy of all images and files that were referenced in the old blogs. Since I planned to delete the old blogs once the process was over, copying media was essential. I then armed myself with patience and a cup of herbal tea while the import plugin did its unnerving thing.

Once each import was done, I visited the new sites and made sure everything was in order.

4. Create subdomain redirects

I now had a suite of sites (e.g. timtom.ch/sub-familynews, timtom.ch/sub-projectx etc.) mirroring the old independent installs of WordPress that were all living in subdomains (e.g. familynews.timtom.ch). Since I wanted all URLs to continue working, I now had to map the old URL structure to the new sites.

I started by renaming each of the old blogs by 1. doing a full backup (or maybe I don’t, but if you’re following this, you should), 2. change their URL in Settings > General (the blog will instantly stop working, but not to worry) and then 3. rename the subdomain they’re operating in accordingly, e.g. in cpanel. I ended up having all my old blogs living at addresses such as projectx-old.timtom.ch. I will likely keep them around for a short while to make sure all is well with the new sites, before deleting them and free up some badly needed database space.

Then it was time for magic. I started by installing the WordPress MU Domain Mapping plugin, setting it up (a file with the slightly worrying name of sunrise.php notably has to be copied away from the plugin directory) and network-activating it.

I then went back to cpanel and created a new “alias” (also known as “parked domains”) for each of the subdomains I needed for my sites. Yes, even though they were all subdomains of timtom.ch (e.g. projectx.timtom.ch), I still needed to treat them as aliases for this to work:

Creating a new alias in cpanel
Creating a new alias in cpanel.

All the aliases I thus created point to the main timtom.ch directory. At first I thought I had to redirect them to the subdirectory in which my main WordPress install lives, but it turned out to be wrong. All subdomain aliases have to point to the home directory of your site for this to work.

As an aside, I found that I was able to make this work only by creating “aliases” using the procedure above. Merely adding a type “A” record in my host’s DNS using cpanel’s “Advanced Zone Editor” didn’t work, probably because the IP address my site uses is shared with other customers. The “alias” function probably makes the required extra settings so that any DNS entries point to the virtual server that’s allocated to me.

Back in WordPress, I then assigned the new subdomains to each of my new sites. The interface to do so is in My Sites > Network Admin > Settings > Domains. Unhelpfully, WordPress MU Domain Mapping’s interface asks for the “site ID” of each site to set this up, which isn’t that obvious to find out. One way I found to identify which ID corresponds to each site is to navigate to the Sites list panel of WordPress Network admin and hover over each site name. The ID will be visible in the URL for each site:

Screenshot of the WordPress Site admin, showing the mouse cursor hovering over a site URL to reveal its ID.
How to identify the site ID of a WordPress Network Site.

Once this is done, the last step was to set up each site’s main URL to the new subdomain, this is done in the Settings > General tab of each site.

Then for a short while none of the new sites worked, which was normal, as the new DNS information didn’t have time to propagate through the Internet yet. This can take up to one hour, depending on settings, so it was a good time to do something else, like starting to work on this blog post!

Once the DNS information was fully propagated, I verified that each of my sites was now working well, each in their own subdomain! I verified that the permalink structure was still the same that I was using for each of my old sites, so that the URLs to the posts and pages were still the same. Migration complete!

I am now the proud owner of a WordPress Network and I can create a new site in a few minutes. All I need once I created the site is go to cpanel, register a new subdomain using the “Alias” function and then assign it to the new site.

5. Fixing HTTPS

There was one extra step in my case since I’m using HTTPS encryption on this website (and you should too) and wanted it to work across all my subdomain WordPress sites too. The certificate I had for timtom.ch did not contain the subdomains I had just created, therefore my browser raised a security alarm when I tried to navigate to my new subdomains using HTTPS. Since I’m now using the Let’s Encrypt cpanel module to handle encryption, the only way to alter the certificate and include my new subdomains was to delete the old certificate and immediately create a new one. I then made sure to include all the new subdomains when creating the new certificate, and bingo, instant HTTPS across all my sites.

There were a few remaining caveats, however. Since the blogs I had just imported were not using HTTPS in the past, all the images I had embedded from Flickr were using HTTP in their <img> tags and thus raising mixed-content errors. I therefore had to go through all the posts that were affected and make sure all <img> tags were using HTTPS.

  1. N.B. all the URLs and directories mentioned here are examples and not actual URLs to anything on this site