Wordpress | Install in Sub Directory *

https://deliciousbrains.com/move-wordpress-root-subdirectory/

In this article, we look at the benefits and limitations of installing WordPress in a subdirectory, how to install and manage subdirectory installs, and how to move WordPress core out of and into a subdirectory for an existing site.

One of the common criticisms PHP developers level at WordPress is that it installs all its core files directly in the web root directory. Modern PHP frameworks like Laravel install their required files as separate dependencies in a vendor directory, each residing in its own specific subdirectory. Not only that, but unlike WordPress, Laravel keeps almost all the PHP files out of a publicly accessible directory, which is very nice for security. This makes for a cleaner, more secure folder structure in the application’s web root.

These modern PHP application directory structures make a default WordPress install look decidedly unprofessional. While it’s not possible to move all the PHP files out of the public web root in a WordPress install, you can customize your installation to mimic this dependency-based development concept by installing WordPress in a subdirectory.

#Table of Contents

  1. The Benefits of Installing WordPress Core in a Subdirectory

  2. How to Tell if WordPress Core is Installed in a Subdirectory

  3. How to Install WordPress Core in a Subdirectory

    1. The Official WordPress Documented Methods
    2. Manually Configure a Subdirectory Install
    3. Using WP-CLI to Install WordPress in a Subdirectory
    4. Using Composer to Install WordPress in a Subdirectory
  4. Considering the WordPress File and Folder Structure

    1. Moving the WordPress Config File
    2. Moving the WordPress Content Folder
  5. Converting a Root Install to a Subdirectory Install

    1. Prepare Your Site
    2. Update the Database
    3. Moving WordPress Files Into the Subdirectory
  6. Converting a Subdirectory Install to a Root Install

    1. Prepare Your Site
    2. Update the WordPress Database
    3. Moving WordPress Out of the Subdirectory
  7. Wrapping Up

#The Benefits of Installing WordPress Core in a Subdirectory

In most web hosting environments, your account is provisioned under a /home/username folder where username is your account username. This folder is sometimes called the top-level or root of your account, and the actual path varies from web host to web host. You will typically have a public or public_html folder configured to serve the website files. This is generally known as the web root directory, and it’s where WordPress is usually installed.

The direct benefit of moving WordPress core files into a subdirectory is a cleaner and more professional-looking directory structure in your web root. Some folks think that switching to a subdirectory install improves security through obscurity. This is only really applicable if you move your wp-config.php file outside of the publicly accessible web root directory, whether you use a subdirectory install or not.

However, in the modern world of automated scanning bots, moving your WordPress core files to a subdirectory might help decrease the chances of malicious bots detecting vulnerabilities when scanning your site. For example, in a typical WordPress install, all your core files are in the public web root, which is accessible from your apex domain (e.g., https://hellfish.media). So all a scanner needs to do is scan your domain name, check for a common WordPress file like https://hellfish.media/wp-blog-header.php, and it will know you have a WordPress install. If you move the core files to a subdirectory, it makes it harder (but not impossible) for attackers to identify that you’re running WordPress.

A cleaner directory structure and an increase in security are sound reasons to move an install into a subdirectory, but there are some things you’ll need to know to accomplish this. You won’t be able to just move the WordPress install to a subdirectory and expect everything to work.

#How to Tell if WordPress Core is Installed in a Subdirectory

With a WordPress install you’ve completed yourself, you’ll likely know if it’s installed in the web root (i.e., a root install) or a subdirectory. However, you might be in a situation where you inherit a site, or use a hosting provider’s one-click installer, or it’s been a really long time since the initial setup.

To determine if your WordPress install is in the web root or a subdirectory, head to your WordPress dashboard and navigate to the Settings > General screen. There you will see the “WordPress Address (URL)” setting and “Site Address (URL)” settings:

Standard WordPress install settings.

If the values match, you have a root WordPress install. If the “WordPress Address (URL)” has an additional path compared to the Site Address (URL), it’s in a subdirectory. You can also tell by looking at the URL when you log into your site. If WordPress was installed in a subdirectory, then the wp-admin page will be shown under that subdirectory (such as hellfish.media/wp/wp-admin/).

#How to Install WordPress Core in a Subdirectory

There are a few different ways you can install the WordPress core files in a subdirectory.

#The Official WordPress Documented Methods

The methods to move WordPress into a subdirectory are detailed in the WordPress.org support documentation. These options are available to you after you’ve already installed WordPress.

The two options are:

  1. Moving a WordPress install to its own directory, without changing the site URL
  2. Moving a WordPress install to its own directory, with a change to the site URL

Neither of these options really help with the increase in security we discussed earlier. The first option moves the site files to a subdirectory and uses rewrite rules in a custom .htaccess file to rewrite requests to the subdirectory. Any bots scanning your URL will still be able to determine that it’s using WordPress.

The second option is even less related to security, and more useful if you have a custom site setup. This is often used where you want to use WordPress to power a blog at a subdirectory of your URL (e.g., https://hellfish.media/blog) and you’ll use something else to power your top-level URL.

That being said, both methods move your WordPress install to a subdirectory, so your web root at least looks tidier.

What would be nice is that first option, but without any URL rewriting for better security.

#Manually Configure a Subdirectory Install

If you recall the earlier description of how the files in a WordPress installation “require” each other, you might have also noticed that in the wp-blog-header.php file, the ABSPATH constant is defined. This constant points to the location where the WordPress core files are installed, no matter where that might be on the file system.

This means that with a little bit of knowledge and some tinkering, we can move all the WordPress core files into a subdirectory, and make a few small changes to let WordPress know where everything is. The process looks like this:

  1. Follow the default WordPress install process.
  2. Once WordPress is installed, create a subdirectory and move all the files from the web root to that subdirectory. To help with “hiding” the core files, you should give the subdirectory an uncommon name, but for our example, we’ll create a subdirectory called wp, and move all the files to the wp subdirectory.
  3. Then, copy (not move) the index.php file from the subdirectory back into the web root directory.
  4. In that newly copied file, update the line that requires the wp-blog-header.php file, to point to the new subdirectory, and save that file: require __DIR__ . '/wp/wp-blog-header.php';
  5. The last step is to update the siteurl value in your WordPress options table— using something like the SQL Buddy plugin or phpMyAdmin—to point to the new subdirectory install, for example, https://hellfish.media/wp.

You’ll note we said “copy (not move)” in step 3. This is important because WordPress needs an index.php file in the install directory (in this case the subdirectory), or it will cause PHP warnings to display on your site or fill up your debug.log file.

Once you’ve completed these steps and all your core WordPress files are in a subdirectory, you will still be able to browse to the front end of your site at your top-level domain (e.g., https://hellfish.media/), but to access your Dashboard you’ll need to browse to wp-admin in the subdirectory (e.g., https://hellfish.media/wp/wp-admin). You might be concerned about losing the changes you’ve made to the copy of the index.php file the next time WordPress is updated. However, WordPress will only update core files in the installation directory, in this case, the subdirectory. So it’s perfectly acceptable to edit the index.php file you copied to the web root directory. Your web root is a little tidier, and it’s a bit harder to determine you’re running WordPress.

While this gives you a small increase in security, we’ll dive into other changes later on to further improve security.

#Using WP-CLI to Install WordPress in a Subdirectory

You’re probably going to want to automate the above process if you are managing your own WordPress installs or you have your own VPS servers. Fortunately, this is possible with a little Bash scripting and WP-CLI. The following script leverages WP-CLI to create a new WordPress site installation with the core files in a specific subdirectory, essentially reproducing everything we did above. It also assumes you don’t already have the database created, and creates it for you. The process will still work if you have an existing database, but will display a MySQL error message that the database already exists.

 

#!/bin/bash

# Installation:

## Download the script to your home directory
# Make sure it has execute permissions (`chmod +x wp-install-core-sub-dir.sh`).
# Install the script in one of the folders in your PATH. (`mv wp-install-core-sub-dir.sh /usr/local/bin/wp-install-core-sub-dir`)

#Usage:

# $ mkdir mysite
# $ cd mysite
# $ wp-install-core-sub-dir {sub-directory} {db_name} {db_user} {db_pass} {site_url} "{site_title}" {admin_user} {admin_pass} {admin_email}

CORE_DIR=${1-'wp'}
DB_NAME=${2-'wordpress'}
DB_USER=${3-'root'}
DB_PASS=${4-'password'}

SITE_URL=${5-'https://wordpress.test'}
SITE_TITLE=${6-'WordPress Site in a Subdirectory'}
SITE_USER=${7-'admin'}
SITE_PASS=${8-'password'}
SITE_EMAIL=${9-'your@email.com'}

# create the dir for the core files
mkdir $CORE_DIR
cd $CORE_DIR

# download WordPress files
wp core download

# create the wp-config.php file
wp config create --dbname=$DB_NAME --dbuser=$DB_USER --dbpass=$DB_PASS

# create the database
wp db create

# install WordPress (less than 5 mins)
wp core install --url=$SITE_URL --title="$SITE_TITLE" --admin_user=$SITE_USER --admin_password=$SITE_PASS --admin_email=$SITE_EMAIL

# Copy (not move) index.php file to root
cd ../
cp "$CORE_DIR/index.php" ./index.php

# Edit index.php to point to correct path of wp-blog-header.php
perl -p -i -e "s/\/wp-blog-header/\/$CORE_DIR\/wp-blog-header/g" index.php


# Update the siteurl in the database with sub directory path
cd $CORE_DIR
wp option update siteurl $(wp option get siteurl)/$CORE_DIR

# Uncomment the below line if you want the config in root
#cp "$CORE_DIR/wp-config.php" ./wp-config.php

echo 'Install finished!'

You can also view this script on Github.

#Using Composer to Install WordPress in a Subdirectory

A popular way to manage WordPress projects is to use Git for source control, and Composer to install WordPress core, the theme, and any plugins as dependencies in the composer.json file of your project.

When you’re ready to deploy your site, you trigger an automated deployment process, which runs composer install to install all your required dependencies.

If you use the johnpbloch/wordpress package, it will install the wordpress-core package in a subdirectory called wordpress by default. You can change this by supplying a custom wordpress-install-dir directory in the extra section of your composer.json. You can then make use of the Composer post-install-cmd event to run the necessary subdirectory changes you need.

 

{
    "name": "polevaultweb/wp-composer-core-sub-dir",
    "description": "Installing WordPress in a subdirectory with Composer",
    "require": {
        "johnpbloch/wordpress": "^5.8"
    },
    "extra": {
        "wordpress-install-dir": "wp"
    },
    "scripts": {
        "post-install-cmd": [
            "cp wp/index.php ./index.php",
            "sed -i \"s/\\/wp-blog-header/\\/wp\\/wp-blog-header/g\" index.php"
        ]
    }
}

As you can see, this replicates the subdirectory process we’ve already outlined earlier. Once the composer install has been completed, WordPress will be installed in the wp subdirectory, the index.php file will be copied to the web root, and edited to update the path to wp-blog-header.php

It is worth noting that the post-install-cmd event only fires after the install command has been executed with a lock file present. It won’t trigger the subdirectory the first time you run composer install locally, as the lock file is only generated after the first time composer install is run.

Unlike the Bash script above, this does not create the wp-config.php file or run the WordPress installation to create the database tables. You can do these manually in your browser, using WP-CLI commands in the post-install-cmd event, or using post deploy scripts if your hosting provider supports them.

#Considering the WordPress File and Folder Structure

As mentioned earlier, moving the core files to a subdirectory allows for a small security improvement, but it’s still possible to determine if your site is running WordPress. This might be a good time to think about any other changes we could make to improve our directory structure and increase security.

Our recommendation is to configure your top-level or root directory structure to look something like this:

 

  /home/username/
    conf/
      wp-config.php
    public/
      content/
      wp/
      index.php
      wp-config.php

We’ve already shown you how to move the WordPress core files into a subdirectory (in this case wp) so let’s see what else we can change, and why.

#Moving the WordPress Config File

The wp-config.php file contains your database credentials, and often any other important API or third-party access keys, but did you know that it’s possible to move this file to a different location from the rest of your WordPress core files?

Woah.

Moving this file to somewhere other than the web root or the subdirectory install is generally considered a much safer choice. The reason for this is that in some cases, problems with the web server could cause it to stop processing PHP files correctly. Common causes for this include a lack of available memory, or a web server misconfiguration. Whatever the cause, if this happens, visitors to the site might be able to view the contents of PHP files including the database credentials in your wp-config.php file. In fact, many would argue that it should be placed even higher up in the directory tree than the root directory.

To achieve this, you would move the wp-config.php file to a different location on the server (in our case we’ll move it to the conf directory just above the public web root directory) and either comment out or delete all the code after the following line:

 

/* That's all, stop editing! Happy publishing. */

We then create a new wp-config.php file in the public web root, with the following code:

 

<?php
require_once __DIR__ . '/../conf/wp-config.php';

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';

WordPress will automatically look for the wp-config.php in the parent folder to where the core files are. By creating our custom wp-config.php file in the web root, we keep the wp folder that contains the core files clean of any custom files. The “true” wp-config.php file is moved to a location that’s not publicly accessible, but where WordPress can still require it, and your database (and other) credentials are safer.

#Moving the WordPress Content Folder

With our WordPress core files in a subdirectory, we could consider moving the wp-content directory too. Themes, plugins, and file uploads should (in most cases) be separate from the core files. This is especially useful if you use version control software like Git to manage your project source code.

WordPress allows us to accomplish this, by setting some WordPress-specific constants, in the wp-config.php file in our wp subdirectory.

 

define( 'WP_CONTENT_DIR', dirname( dirname(__FILE__) ) . '/content' );
define( 'WP_CONTENT_URL', 'https://' . $_SERVER['HTTP_HOST'] . '/content' );

This configuration tells WordPress to expect to find the content directory in a directory called content in the web root. It’s inspired by Mark Jaquith’s WordPress skeleton, with minor tweaks to match our directory structure. As Mark points out in the GitHub readme, it’s cleaner and the content directory is no longer in the scope of the wp directory.

If you make this change on an already existing site, you will need to move the contents of your wp-content directory (plugins, themes, uploads, etc.) to the new content directory. You will also need to run a Find & Replace on all your data. The goal is to update URLs for your media and asset files to point them to the new content URL, as well as ensuring any saved file paths are now pointing to the new content path.

If any external sites are linking to any of your media or assets, you’ll want to set up a path redirect, so that those links don’t throw 404 errors. This can be achieved by setting a path redirect using regex with a matching string of ^/wp-content/(.*) and setting the redirecting string to /content/$1. We recommend setting up this redirect at the web server level for best performance. If using SpinupWP, this can be done via the Path Redirects screen:

 

ts screen:

Setting up a path redirect on SpinupWP.

If you’re unable to set up a redirect at the web server level, the Redirection plugin is the next best thing.

#Converting a Root Install to a Subdirectory Install

Installing the WordPress core files when starting a new site is straightforward enough, but converting an existing root install to a subdirectory install requires some care. It’s not simply a case of creating a subdirectory in your web root and moving all the WordPress files there. You also need to take into account any URLs or paths stored in the database that points to your root install directory. We cover the details of this in our WordPress Core Installed in a Subdirectory help doc, but let’s look at a simple example.

I’ve set up a local site, https://hellfishmedia.test, as a WordPress root install.

Root directory install.

I’ve also created a very basic page to serve as the site home page, which includes an image in the page content, and a link to a Contact page.

Hellfish Media Home Page.

#Step 1: Prepare Your Site

If this is a live site, it’s a good idea to show a maintenance message while you make changes that could break the site. For this, we recommend the WP Maintenance Mode plugin. Install it, activate maintenance mode in the plugin settings, and your users will see a message letting them know about the brief period of downtime.

Maintenance message.

Front-end users will only see the maintenance page, but we can still access the WordPress dashboard through https://hellfishmedia.test/wp-admin/.

#Step 2: Update the Database

For our example, we’ll be moving WordPress into a new /wp subdirectory, so you need to update the database to reflect that change – but only for URLs that need it.

You only need to worry about references to image files and assets contained in theme or plugin folders. All of those are located under /wp-content by default, or a custom content path, if you’ve configured one. As such, you can use the content path to target URLs and file paths you need to change. If you’ve configured a different location for your content as we described earlier, you might need to tweak this to match.

This is possible using any decent find and replace tool, including the WP-CLI search-replace command, Better Search Replace, or WP Migrate DB. For our purposes we’ll be using WP Migrate DB Pro‘s Find & Replace tool.

In our example site, we’ll want to find //hellfishmedia.test/wp-content and replace it with //hellfishmedia.test/wp/wp-content, and find /home/iain/development/websites/hellfishmedia/wp-content and replace it with /home/iain/development/websites/hellfishmedia/wp/wp-content to update URLs and file paths respectively. For the sake of the test, we’re also replacing the site title. Here’s what this will look like in WP Migrate DB Pro:

Custom find & replace with the specified changes.

What’s great about the WP Migrate DB Pro Find & Replace tool is that you can preview the changes before you run them.

Custom find & replace preview.

You can also inspect what updates the plugin will make on a per-table basis.

Once you run the find and replace, and take a look at any page content, you’ll see that while links to the Contact page don’t include the subdirectory, the embedded image does:

Showing the updated content URLs in the code editor.

Your next step is to update the “WordPress Address (URL)” setting in your “General Settings” page to include the subdirectory. You can do this by navigating to Settings > General in your WordPress admin, and adding the subdirectory to the “WordPress Address (URL)” field. This will break the backend of your site until you complete the rest of this process, so make sure you don’t need to access it for a bit.

Updating WordPress address.

Once you save the change, your WordPress admin dashboard will show you a “Page not found” error and your maintenance page will lose all its styling. This happens because WordPress is still looking for all its admin and asset files under https://hellfishmedia.test/wp/, but they don’t exist.

#Step 3: Moving WordPress Files Into the Subdirectory

While the maintenance page won’t look pretty at this point, at least it displays the maintenance message to your site users. Unfortunately, that’s going to break in the next step, as you need to move the files from the web root into the correct subdirectory. For our example, we’ll do the following:

  1. Create the wp subdirectory in the web root.
  2. Move everything from the web root into the wp directory.
  3. Copy (not move) the index.php file and the .htaccess file from /wp back into the web root directory.

You can do this via your local file manager, or via an FTP client if it’s a remote server.

Video Player

00:00

00:21

Alternatively, if you prefer the terminal, the commands would look like this:

 

cd ~/development/websites/hellfishmedia
mkdir wp
mv * .* wp/
cp wp/index.php .
cp wp/.htaccess .

This will cause the maintenance page to display an error.

 

Warning: require(/home/iain/development/websites/hellfishmedia/wp-blog-header.php): Failed to open stream: No such file or directory in /home/iain/development/websites/hellfishmedia/index.php on line 17

Fatal error: Uncaught Error: Failed opening required '/home/iain/development/websites/hellfishmedia/wp-blog-header.php' (include_path='.:/usr/share/php') in /home/iain/development/websites/hellfishmedia/index.php:17 Stack trace: #0 {main} thrown in /home/iain/development/websites/hellfishmedia/index.php on line 17

The last step is to update the require expression in the index.php file in the web root with the path to the new subdirectory, as we showed you right at the beginning of this guide. Edit the file in your code editor, and update the line to look like this:

 

require __DIR__ . '/wp/wp-blog-header.php';

Alternatively, if you prefer to run a terminal command, you can use the same perl command that our WP-CLI script used earlier:

 

perl -p -i -e "s/\/wp-blog-header/\/wp\/wp-blog-header/g" index.php

Your root install should now be fully converted to a subdirectory install. You should take this opportunity to check all your content, make sure everything appears to be working, and that images display as you’d expect.

It’s also a good idea to log into your dashboard and navigate to Settings > Permalinks to save and flush your permalinks.

One last thing to note is that the example site we set up here doesn’t have a lot of plugins or a custom theme. These aspects could add additional complexities when converting between root and subdirectory installs. For this reason, we recommend making sure you’ve thoroughly tested the site after converting it from one install type to another.

#Converting a Subdirectory Install to a Root Install

If you’ve completely converted to running all your WordPress sites as subdirectory installs, you might come across a situation where you need to work with a live client site that is a root install. If you use a tool like WP Migrate DB Pro, it’s not possible to migrate from a subdirectory install to a root install. This means you might need to first convert a local dev or staging site to a root install before migrating it to the live site. Converting a subdirectory install to a root install is almost always easier than the reverse because the URLs and paths are already using the subdirectory path you’re converting from. To do this, you’ll need to update both the database and the file structure, ideally with a minimal amount of downtime.

I’ve set up a local site, https://hellfishmedia.test, with WordPress installed in a subdirectory named wp.

WordPress subdirectory install.

I’ve also created a very basic page to serve as the site home page, which includes an image in the page content, and a link to a Contact page.

Hellfish Media Home Page.

#Step 1: Prepare Your Site

Converting the site is going to require some database changes but before you do this you’ll want to ensure that you can still access wp-admin. To achieve this, define the site URL and home URL values in the wp-config.php file. This will override the settings for siteurl and home in the wp_options table and will prevent you from losing access to the WordPress dashboard.

 

// The URL where the WordPress application files are accessible
define( 'WP_SITEURL', 'https://hellfishmedia.test/wp' );
// URL where the front end is accessible
define( 'WP_HOME', 'https://hellfishmedia.test' );

If the subdirectory site is live, it would be a good idea to show a maintenance message while you make changes that could break the site. For this, we recommend the WP Maintenance Mode plugin. Install it, activate maintenance mode in the plugin settings, and your users will see a message to let them know about a brief period of downtime, rather than a broken site.

Maintenance message.

However, we can still visit the dashboard using https://hellfishmedia.test/wp/wp-admin/.

The next step is to update the database.

#Step 2: Update the WordPress Database

Now you can update the database to remove any references to the subdirectory. This is possible using any decent find and replace tool, including the WP-CLI search-replace command, Better Search Replace, or WP Migrate DB. For our purposes, we’ll be using the WP Migrate DB Pro Find & Replace tool.

To start, we search for the source site URL, including the subdirectory (//hellfishmedia.test/wp) and replace it with the URL excluding the subdirectory (//hellfishmedia.test). We also want to update any physical paths from /home/iain/development/websites/hellfishmedia/wp to /home/iain/development/websites/hellfishmedia. We’re also updating the site title for demonstration purposes:

Custom find & replace with the specified changes.

As we mentioned earlier, WP Migrate DB Pro’s Find & Replace tool lets you preview changes before you run them.

Find & replace preview.

You can even inspect what updates the plugin will make on a per-table basis. Here I can confirm that the link to the image in the wp_posts table for my Home page will be updated, as well as the GUID for the image attachment. If I don’t want to update the GUID, I could alter this in the settings. In this case, it makes sense to change the GUID.

Find & replace differences.

You might not notice any changes after you run the find and replace. The front end will still show the maintenance page, while the back end will still work because of the hardcoded constants.

However, if you look at some post content, you’ll notice that the images are broken and internal links now lack the subdirectory. Here is the home page in the Block Editor:

Editing the home page.

To fix this, there’s one last step we have to do.

#Step 3: Moving WordPress Out of the Subdirectory

Your last step is to manually move all the files in the subdirectory into the public web root directory of your site.

You drag and drop the files in your local file manager or use an FTP client if you’re working on a remote server. However, be sure to select the option to overwrite the index.php file:

Moving the files in the file manager.

Once you’ve copied over the files, you can delete the wp subdirectory.

You can also do this on the command line, but if your site is on a remote server you need to SSH into the server first. In this case, I’m working with my local hellfishmedia directory that we ran the find and replace on earlier.

 

cd ~/development/websites/hellfishmedia
rm index.php
cp -R wp/. .
rm -rf wp

The last piece is to remove the WP_SITEURL and WP_HOME constants from your wp-config.php and then test your site.

It’s also a good idea to log into your WordPress dashboard and navigate to Settings > Permalinks. Here, click the Save Changes button to make sure you flush the permalinks and that your URLs still work.

#Wrapping Up

Hopefully, we’ve given you enough information to consider switching your root installs to subdirectory installs, as well as the tools to do so. If you’re looking for a modern, Composer-based option, you could consider using a similar structure as our SpinupWP spinupwp-composer-site package, which takes the idea of installing WordPress as a dependency to a whole new level.

Do you run your WordPress installs in a subdirectory? What workflows do you use to manage your WordPress directory structure? Do you think WP Migrate DB Pro should do more to make the migration process between the root and subdirectory more straightforward? Let us know your thoughts in the comments section below!

This entry was posted in WP Migrate DB Pro, Workflow and tagged WordPress, Migration Tools, Development, Deployment, subdirectory.

ABOUT THE AUTHOR

img

Iain Poulson

Iain is a product manager based in the south of England. He also runs multiple WordPress products. He helps people buy and sell WordPress businesses and writes a monthly newsletter about WordPress trends.

@polevaultweb polevaultweb.com

Scroll to Top