Tell me if this sounds familiar: You pick up a new client who wants you to develop a new theme for their site so you set up a development site and get to work. A few days go by and you’ve laid the groundwork of the new theme and you think it’s about time to pull in their current site’s content so that you can get into the specifics. You set up WP Migrate DB Pro on both sites, pull the database and then realize that your development site was a subdirectory install while the client’s live site was a standard root-directory install and your dev site is totally messed up.
One of WP Migrate DB Pro’s few gotchas is the fact that it doesn’t support migrations between standard WordPress installs where WordPress’ files are installed in the root of your public folder and subdirectory installs where WordPress’ files are installed in a subdirectory of the public folder. At first, this may seem like an arbitrary restriction, and many users don’t even realize that their two sites are installed differently in the first place, so let’s take a few minutes to look at how you can tell if your WordPress site is a root or subdirectory install and why it’s difficult to migrate between the two.
How to tell if WordPress is installed in a subdirectory
If you installed WordPress manually, you probably know if you did a root or subdirectory install, but maybe you inherited a site or used a one-click installer provided by your host or maybe it’s just been a really long time since you first set up the site. If this is the case, all you need to do is log in to your WordPress admin and navigate to Settings > General. There you will see the “WordPress Address (URL)” setting and “Site Address (URL)” setting. If those two settings are the same, then you have a root WordPress install:
And if those settings are different, then you’re probably looking at WordPress installed in a subdirectory:
In this case, if you started off at the front end of your site at example.com, you would also probably notice that when you log in, you’re logged in to a URL that’s a subdirectory of example.com like example.com/wp/wp-admin/.
Why can’t I migrate between the two?
What makes migrating from a root to a subdirectory install difficult is that in a root WordPress install, all links and asset references use the same URL, but in a subdirectory install links to posts or pages will use the site URL while links or references to media or assets use the WordPress URL. So running a simple find/replace where you replace
//example.com is no longer possible since some instances of
//example.local need to be replaced with
//example.com while others need to be replaced with
Migrating from a subdirectory install to a root install actually isn’t as difficult since you just need to make sure that all URLs and paths that reference the subdirectory are replaced with the main URL or path. The real issue here is that if you need to migrate back to your subdirectory install, things get tricky again, so you’re much better off making sure that all of your sites use the same installation scheme before you start migrating between them.
Additionally, you’re usually migrating between a production or staging site and a development site and all of these environments should mirror each other. If your production site is installed in a subdirectory, but your development site is installed in the public root, then a change you make on your development site might break when it’s migrated to the production site – especially if that change is made in code rather than in the database – since you’d still need to run a find and replace on the database to update URLs for the different structure, but your code does not get the same treatment.
Converting a subdirectory install to a root install
Converting a subdirectory install to a root install is almost always easier than the reverse if your situation allows it.
In order to convert a subdirectory install to a root install, we’ll need to update both the database and the file structure, ideally with a minimal amount of downtime.
Step 0: Define the problem
Before we do anything, let’s figure out exactly what would happen if we tried to just migrate from a subdirectory install to a root install with the default WP Migrate DB Pro settings (plus an additional replacement for the site title so we can tell them apart)
Here I’m migrating from
wp-in-a-subdirectory.loca which has WordPress installed in the
/subdir directory to
wp-standard-install.local which is a root install.
And here are the (completely expected) results:
wp-standard-install.local/subdir. Also, if I try to log in to the wp-admin, WordPress issues a redirect to
Step 1: Prepare
Before we actually change anything in the database, we’ll want to make sure that doing so won’t cause any issues with access to wp-admin during the process. We can do this by defining the site and WordPress URLs in our wp-config.php before running the migration. This will override the corresponding database settings which will prevent us from getting kicked out of wp-admin after running a find/replace migration.
// WordPress Address (URL) define( 'WP_SITEURL', 'http://wp-in-a-subdirectory.local/subdir' ); //Site Address (URL) ¯\_(ツ)_/¯ define( 'WP_HOME', 'http://wp-in-a-subdirectory.local' );
Now we can also modify the
index.php file at the root of the site to show a maintenance message. Just comment out the line with
require( dirname( __FILE__ )[...] close the php tag and write a nice message to your users about the brief downtime that’s about to occur.
/** * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * * @package WordPress */ /** * Tells WordPress to load the WordPress theme and output it. * * @var bool */ define('WP_USE_THEMES', true); /** Loads the WordPress Environment and Template */ // require( dirname( __FILE__ ) . '/subdir/wp-blog-header.php' ); ?> <!DOCTYPE html> <html lang="en"> <head> <title>Maintenance</title> </head> <body style="text-align: center; font-family: sans-serif;"> <h1>This site is down for maintenance.</h1> <img src="https://media.giphy.com/media/G0SobCKnymYJq/giphy.gif" alt="Under construction" > </body> </html>
Now anybody who visits our website will see this:
But we can still visit the wp-admin by visiting
Step 2: Update the DB
Now all we need to do is update the database to remove any references to our subdirectory, we can do this with WP Migrate DB Pro’s Find & Replace feature by searching for
//wp-in-a-subdirectory.local/subdir and replacing with
//wp-in-a-subdirectory.local, we’ll also want to run a replacement on the file path, just in case there are any file references stored in the db, for this site we’ll search for
/app/public/subdir and replace with
/app/public (and I’m updating the site title again for demonstration purposes):
Once we’ve run this migration, not much will appear to have changed. The front end is still showing the maintenance page and the back end still works because we’ve hardcoded the
WP_SITEURL constant to still use the subdirectory, but if you look at some post content, you’ll notice that your images are broken and internal links now lack the subdirectory:
Step 3: Moving WordPress Out of the Subdirectory
The first thing we’ll do now is remove the
WP_HOME definitions from our
wp-config.php. You could also just update
WP_SITEURL to remove the
/subdir part, but removing them altogether works just as well.
Once you’ve done that, your wp-admin will no longer work, so let’s get straight to moving the files. All we need to do is move everything that’s in the
subdir directory back into the public root directory of our site.
You can do this on the command line or over SSH if your site is on a remote server with a command like this:
rm index.php && mv subdir/* && rm -rf subdir
Or you can drag and drop the files in your browser’s file explorer or in an FTP program if you’re working on a remote server, just be sure to select the option to overwrite the index.php file:
Now your site is no longer in that dreadful subdirectory and you can migrate to your other root install without issue. You can visit
http://wp-in-a-subdirectory.local to confirm that everything works:
It’s also a good idea to log into your wp-admin (now located at
http://wp-in-a-subdirectory.local/wp-admin instead of
http://wp-in-a-subdirectory.local/subdir/wp-admin) and navigate to
Permalinks and click the
save changes button just to make sure your permalinks are flushed and your URLs will still work.
Converting a Root Install to a Subdirectory Install
Many WordPress developers will tell you that subdirectory installs are a best practice and we would probably agree as that’s how we would recommend setting up a new WordPress project. But converting an existing root install to a subdirectory install is a bit harder since all of our URLs in a root install are the same but we have two different URL structures on the subdirectory install, one for the links and another for media files and theme assets. That means that the find/replace that we run on the database will be a bit more complicated and we’ll probably also want to do some manual checking through our site after everything is said and done just to be sure that we didn’t miss anything.
Step 0: Define the Problem
Before we do anything, let’s figure out exactly what would happen if we tried to just migrate from a root install to a subdirectory install with the default WP Migrate DB Pro settings (plus an additional replacement for the site title so we can tell them apart)
Here I’m migrating from
wp-standard-install.local which is a root install to
wp-in-a-subdirectory.local which has WordPress installed in the
/subdir directory. Also, if you’re following along,
wp-in-a-subdirectory.local has been restored since we converted it to a root install in the last section.
And again here are the (completely expected) results:
Similar to when we migrated a subdirectory install to a root install, migrating this root install to a subdirectory install hasn’t completely borked the site; the links still work as expected, but all references to images or stylesheets are missing the subdirectory. So before we can run this migration we need to convert our root install to a subdirectory install.
Step 1: Prepare
Before you embark on this process, you’ll probably want to see what the codex has to say about giving WordPress its own directory. Since we’re converting an existing site, we have to consider the fact that we already have content, but other than that this process is very similar and we can prepare our site by defining the site and home URLs in wp-config.php and throwing up a maintenance page, just like we did before.
In this case, we don’t need to set the
WP_HOME constants as our search/replace won’t touch those and we’ll need to update the WordPress address setting manually a bit later.
We can, however, add the same code as before to the under construction page. The only difference here is that the index.php file hasn’t yet been modified to point to the subdirectory:
<?php /** * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * * @package WordPress */ /** * Tells WordPress to load the WordPress theme and output it. * * @var bool */ define( 'WP_USE_THEMES', true ); /** Loads the WordPress Environment and Template */ // require( dirname( __FILE__ ) . '/wp-blog-header.php' ); ?> <!DOCTYPE html> <html lang="en"> <head> <title>Maintenance</title> </head> <body style="text-align: center; font-family: sans-serif;"> <h1>This site is down for maintenance.</h1> <img src="https://media.giphy.com/media/G0SobCKnymYJq/giphy.gif" alt="Under construction"> </body> </html>
Now visitors will only see the maintenance page, but we can still access wp-admin by visiting
Step 2: Update the Database
We’ll be moving WordPress into the aptly named
/subdir subdirectory so we need to update the database to utilize the subdirectory, but only for the URLs that will be contained by the subdirectory. Earlier we saw that it’s only references to image files and potentially any references to assets contained in theme or plugin folders that need to be updated and all of those are under
/wp-content, so we can use that to target the URLs that we need to change by searching for
//wp-standard-install.local/wp-content and replacing it with
Additionally, we’ll want to replace any strings containing the file path to utilize the subdirectory, and here we can search for our public root file path
/app/public and add the subdirectory
/app/public/subdir, since we’re moving everything into the subdirectory. Here’s what that will look like using WP Migrate DB Pro’s Find/Replace migration (and again including an update to the site’s title):
And again, if we check out the post content after we run this migration, we should see that our links to other pages on the site don’t include the subdirectory, but any embedded images do:
Now that our database is updated, we can work on moving WordPress into a subdirectory. For the most part we can just follow the process outlined under “Method II (With URL change)” in that codex article I mentioned earlier – but I’ll also go over that here.
The first thing you’ll need to do is update the WordPress address (URL) setting to use the subdirectory. This will break the back end of your site, so be sure that you’re done working on the admin for now and then head to Settings > General and add your subdirectory name to the end of the WordPress address:
Once you scroll down to the bottom of this page and click “save changes” you’ll get booted out to your maintenance page. This happens because WordPress now thinks that all of the wp-admin files are under the
/app/public/subdir/wp-admin so it tried to forward us to
http://wp-standard-install.local/subdir/wp-login.php but that file doesn’t exist yet, so we’re getting shown the maintenance page instead.
Step 3: Moving WordPress Files Into the Subdirectory
Now that our site is completely unusable, we need to fix it by moving the files to where WordPress expects them to be. The first thing we’ll do is create the
subdir subdirectory in our public root, and then we’ll move everything into it. Next we’ll copy the index.php and .htaccess files from
/subdir back into the public root directory so that our maintenance page continues to show while we finish up.
Next, we can restore the index.php within the subdirectory to its factory state by removing the maintenance html and uncommenting the
require( dirname( __FILE__) [...] line:
<?php /** * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * * @package WordPress */ /** * Tells WordPress to load the WordPress theme and output it. * * @var bool */ define( 'WP_USE_THEMES', true ); /** Loads the WordPress Environment and Template */ require( dirname( __FILE__ ) . '/wp-blog-header.php' );
Now you should be able to access your WordPress admin again by navigating to
You can once again take this opportunity to check on all of your content to make sure everything appears to be working correctly, and that images are displaying properly.
Now all that’s left to do is update the
index.php file that’s in our public root to take the site out of maintenance mode as well as update the require statement to require
wp-blog-header.php from its new location within our new subdirectory. That file should look like this:
<?php /** * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * * @package WordPress */ /** * Tells WordPress to load the WordPress theme and output it. * * @var bool */ define( 'WP_USE_THEMES', true ); /**Loads the WordPress Environment and Template From Within ./subdir */ require( dirname( __FILE__ ) . '/subdir/wp-blog-header.php' );
Now your site is no longer stuck with that dreadfully flat file structure and you can migrate to your other root install without issue. You can visit
http://wp-standard-install.local to confirm that everything works:
And again, it’s also a good idea to log into your admin and navigate to
Permalinks and click the
save changes button just to make sure your permalinks are flushed and your URLs will still work.
Now you’re probably thinking to yourself, this entire article is predicated on the fact that WP Migrate DB Pro doesn’t support migrating from a root install to a subdirectory install (or vice versa), but we used WP Migrate DB Pro to convert these sites in place, so what gives?! Can’t I just run migrations that do the correct find/replace and call it a day?
You certainly could run migrations back and forth between sites that are installed differently, but you really need to know everything that’s different about them, and while the sites we converted in this article had content that needed to be updated, they didn’t have a lot of plugins which might require some additional attention. At the end of the day, it’s probably possible, but since WP Migrate DB Pro can’t really do this for you automatically, the smart move is to first make sure that both of your sites are installed the same way before you start running migrations between them. Having your sites directory structure match not only makes migrations easier, it also helps you to be more confident that things will work the same on both sites as you’re developing and deploying.
Do you agree that both of your sites should use the same directory structure? Or can you think of a good reason that you’d want to migrate between two sites that were installed differently? We’ve been debating this in a github issue for nearly 3 years now:
So maybe it’s time that we pose the question to you: Do you think that WP Migrate DB Pro should support migrating between sites that are installed differently, or do you agree with our current stance that you should convert one of your sites first before you start moving data back and forth between them? Let us know in the comments!