Migrating WordPress from one URL to another (with Dreamhost)

I haven’t seen a comprehensive guide for this so I figured I should probably write it up.

A site on my Dreamhost Shared Hosting Server was compromised a few days ago, and even though my security is decent, I figured it was time to switch up the configuration for my 4 WordPress blogs. The goal I had was to move each blog to its own subdomain of malcolmgin.com, to change the admin account names and passwords, to change the author/editor passwords, and to reset any 2 factor authentication codes for the accounts.

PLEASE NOTE: URLs and file paths and user names and passwords and db table name prefixes and all other kinds of information in this post are all simply for example, for illustration purposes. There is nothing at https://blog.malcolmgin.com, and there are no hostnames or databases or usernames named what they are in this post. That said, I tried my hardest to keep these examples self-consistent, so if I use a particular username for the new blog’s database in one part of the post, it’ll be the same when I refer to it again in a later part of the post.

This partitioning into different subdomains also allows me to change the execution credentials (the UNIX user under which Apache executes), and the database credentials (the MySQL user which WordPress uses to get the database-stored data for the site). So any credentials that may have been stolen by the hack before Dreamhost Security Bot stopped it will be useless.

Another goal I had was, once I migrated the WordPress sites, to use .htaccess directives to redirect folks coming in to the old URLs for the old sites to the new subdomain-hosted sites. I do this because it’s important to me not to break the web. I think that old URLs should work even as we migrate and move forward to new URLs.

So let me break this all down for you, in case you need the guide for something.

You can use this as a checklist! Checklists are GOOD for geeking as a trade.

This is written for using Dreamhost’s panel, but I’m pretty sure you can do this or a form of this on most internet hosting services.

Prepare your Blog’s new home

Use Dreamhost panel’s tools to create the new spot for your soon-to-be-migrated blog:

Log into Dreamhost’s (control) panel.

Create the subdomain.

Domains > Manage Domains.

Click Add Hosting to a Domain/Subdomain button.

Domain to host: blog.malcolmgin.com

Do you want the www in your URL?: Remove WWW: Make http://www.example.com/ redirect to http://example.com/

Run this domain under the user: Create a New User

Write down the new user name

PHP mode: PHP 7.0 FastCGI (default)

HTTPS: Check

You’ll get an email you should probably keep or record somehow with the private key Let’s Encrypt generates. Mostly in case you need to move the site again before the SSL cert expires.

Keep everything else as is.

Click Fully host this domain button.

Write down the domain user’s generated password from the next screen. You may want to change it later – the default random password length pretty short. I’ll leave it as an exercise to figure out how to change it – but it doesn’t affect anything but the domain and Apache, and DreamHost handles the switchover if you do change the password.

While you’re waiting for the WordPress site domain to be created, go ahead and create the new database host name (optional) and database (required). Create the new database host name (optional):

Goodies > MySQL Databases.

Click Add New Hostname button.

New Hostname: db-blog.malcolmgin.com (or if you want, obfuscate this database name – only your WordPress install and Dreamhost need to know it). Anyway, keep track of it so you can use it when you install WordPress.

Click Create this MySQL hostname now! button.

If you did create a new database host name, wait for it to show up on the list of hostnames. You can click the link again in the navigation to reload it until it shows up.

Once the hostname is created (or if you decide to reuse an existing hostname), create the empty database:

Goodies > MySQL Database. Scroll all the way down to the end of the page to see the Create a new MySQL database: form.

Database Name: mysqlblogwp (whatever you like – you can obfuscate this too if you like) Note this for later setup steps.

Use Hostname: db-blog.malcolmgin.com (From 3.2.1, or an already existing hostname – these are interchangeable on Dreamhost Shared Servers).

If you choose “Create a new hostname now” under Use Hostname, there are additional options.

First User: Create a new user now… (Since one point of this exercise is security, create a new user. Give it a random username (e.g. abcdefgh – just characters and numbers, to be careful), and a long, secure password (e.g. %4wh1*3zb@) with special characters, letters, and numbers.) Note this username and password – I don’t think you have to use it anywhere unless you do this migration the hard way. See below for details on that.

When the domain name, database host, and database are set up, you can create an empty blog at the new site. With Dreamhost, this is another Goodies panel action. With other hosts, you may have to manually install it, or request it some other way.

Install to: blog.malcolmgin.com (Use the drop-down to select the domain name you created in step 2.2)

Select database: mysqlblogwp (Use the drop-down to select the database name you created in step 5.1.1)

Deluxe install: UNCHECK

Click the Install it for me now! button.

Dreamhost will take up to 10 minutes to complete this installation. When it does, it’ll send you an email, and when you receive the email you should at least do the first step: Set your password for your first (Admin) user, and record the randomly created username and whatever password you set, since you’ll need to log in soon to set up the Blog for Import or Restore. But while you wait, you can back up and export your old blog.

Back up and Export your Old Blog

While Dreamhost is busy installing the empty WordPress blog on your new subdomain, you must back up (keep a copy of) your old blog. What you should focus on is:

Keep a copy of the files.

Export the Blog with built-in tools.

Back up the database.

Exporting and backing up are very similar. If you Export, you can try the Import method on the new Blog. This doesn’t always work (out of 4 blogs I migrated recently, Export/Import only worked for 2 of them), so it’s good to have a database backup too, just in case you need to restore the database backup. Restoring is more reliable than importing, but also more finicky.

The other thing to note, because you are migrating to an entirely new location (and presumably, set of directories on your host), you have a de facto backup in the form of the old URL. As long as you don’t delete it or its database or make it inaccessible (with an .htaccess redirect) too soon, you can go back to the old site’s URL or files or database to recapture backups if need be.

For security, you very much should plan to delete the old site, but while you are migrating, keep it around, just in case.

(Note: for ease of references, I’m continuing the numbered list from above.)

Keep a copy of the files.

For this you need some local storage. This is most useful if the Export/Import step doesn’t work. Primarily you are concerned with the ./wp-content/uploads folder and subfolders (for the Media Library), as well as with preserving the wp-config.php settings file and .htaccess, if it exists for your old blog. But it doesn’t hurt to, if you have the bandwidth and local space, download a copy of all the files. FWIW, running WordPress version 4.9.6, the full copy of files for my busiest/oldest blog and all the media library content was 300 MB, but this figure will change depending on how many uploads you have.

Set up a local folder to put your file backup into.

Configure an FTP client (I use FileZilla) to connect to your old blog’s filesystem. Key settings for DreamHost:

Select all the files in this directory (e.g. wp-config.php and directories like wp-content) and transfer them to your local storage folder. This transfer may take a while to complete. There are thousands of files. For my four blogs, this step took about 15 to 20 minutes.

(Optional) copy only selected files. See the WordPress codex’s documentation for Upgrading WordPress to find out which files are important. Part of the reason I’m writing this guide blog post is that my files and folder structure were a little different, but the official documentation can help too.

While the FTP file copy is under way, you can also work on getting your other backups going. First, Export from WordPress.

Now you need to prepare your new, empty blog for Import or Database Restore.

Preparing Your New Blog for the Data and Content

At this point, you don’t know if the Export/Import operation is going to work or whether you’ll have to resort to Database restore. Either way, though, the new blog needs to have the same or similar plugins, and the same theme installed and active so the migration works and the media and look and feel are preserved.

I usually put two browser windows side by side on my screen. In one window, I open my old blog, and in the other window I open the new, both logged in with an Admin user. This way I can view the plugins on the old site and add them all to the new. And the same with the themes.

A tip for if you have a lot of plugins to install and activate: Once I’m in the Add New interface in the new site, I will search for and install all the plugins I want in the new site, but I won’t activate any of them until I’m done installing them. This is because activating a plugin from the Add New page will dump you out of the Add New pages and into the plugins page. But installing won’t. Once I’m done installing all of the plugins, I will go to the Installed Plugins page, click the “Inactive” link near the top, click the upper checkbox to select all of them, use the dropdown Bulk Actions to choose the Activate action, and click the Apply button. This is faster and more efficient than Installing and Activating the plugins one at a time.

For security, I recommend only having one theme installed and active at a time. Since you’re migrating from an old site, you already have that site’s theme picked out, so you may just want to install and activate that theme, then delete the old theme(s) that you’re not using that were installed with the new site.

Now that your new blog is prepped, it’s time to try out the import and restore options.

Try to Import Your Export First

Importing is a great deal easier than restoring the database, so it makes sense to try it first. Also, if it doesn’t work and you have to restore, it won’t matter that you tried (and failed) to import first.

Importing does a number of things for you for free:

Importing brings your data over to the new database with the right table names for the new database. This is important because one layer of security for DreamHost WordPress blogs is that DreamHost creates the tables with a random prefix (e.g. wp_1z2y3w) for every table. This helps prevent hacks that rely on knowing your database structure.

Importing works with your new blog’s user setup, or helps you set up new users to map authorship of old posts and pages and other content to. If you have to restore the database, that recreates your old users, and that’s a hassle because part of what you’re trying to do here is secure your site by creating and using new users and passwords.

Importing can automatically populate your new blog’s media library with the old media. Instead of having to use FTP to restore your old media, the Import process can download from the old site.

Import your Export and see if it works.

Log into your new site’s Admin Dashboard with an Admin user: https://blog.malcolmgin.com

Choose Tools > Import

Click the Install now link under “WordPress”.

Click the Run Importer link.

Upload the Export XML file to the page.

Select or create new users to map content from the old blog to users on the new blog.

Select the checkbox to Download and Import File Attachments.

If the import is successful, WordPress will tell you so.

If your import was successful, you should have your old content in your new blog with the new username logins, and all your Media Library content should be restored. Skip Restoring Your Site the Hard Way (with Database Backup and FTP) and head on down to the Try Logging In and Cleanup Tasks sections further down. Scroll a while The Hard Way’s kind of long.

So what if your import wasn’t successful? Usually I get redirected, on the new blog, to a page that doesn’t exist. Sometimes I also get an error. Whatever the case, if your import failed, then you have to do it the hard way.

Restoring Your Site the Hard Way (with Database Backup and FTP)

The first thing to know is, especially on DreamHost and other hosts (MediaTemple also does this) that add a random prefix to the database tables in the databases that you install WordPress to, the database your new blog wants to use and the database your old blog uses are different, at least by table names.

Another thing to know is that the MySQL backup you create will not only have the old database table names in it, but it will also have many URLs in the database content that refer to the old blog’s URLs. These will be a mix of static pages, like a home page, and media links, as well as any links you put manually into old posts or pages.

Finally, know that restoring your site with a database backup and restore will reinstate the old users and any, e.g., Authenticators (for 2 factor authentication) or other security measures associated with the old users from the old blog. This is okay. The new blog will generally work for logins like the old blog. The issue is that since you’re doing this migration to forestall security issues by changing accounts, users, passwords, etc., you’ll probably want to go back to how you originally set up the new blog.

Because you are migrating to a new site, you can leave the new site’s wp-config.php file alone. But you do need to hand-edit the MySQL backup file (make a copy before you start! do these edits to the copy!), and you need to set up FTP to the new site so you can copy over the media library files.

Information you need before you start this step:

Prefix for table names in old blog: wp_1a2b3c_
You can get this by opening the SQL Backup working file and looking at the table names, e.g. if the first few lines contain a comment, -- Table structure for table `wp_1a2b3c_commentmeta` then your prefix is wp_1a2b3c_

Prefix for table names in new blog: wp_1z2y3w
You should probably get this by opening your new database in phpMyAdmin which you can access in the DreamHost panel at Goodies > MySQL Databases, then clicking phpMyAdmin for the database host name you created in Step 3.2.1. In the login screen specify, again, the database host name from 3.2.1, and use the credentials you created in Step 5.1.3. For purposes of this writeup/exercise:

Click phpMyAdmin next to hostname: db-blog.malcolmgin.com

Log in with:

MySQL Hostname: db-blog.malcolmgin.com

Username: abcdefgh

Password: %4wh1*3zb@

Once you’re logged in, click the plus sign next to your database name: mysqlblogwp and note the table prefix.

Your old blog’s URL: http://www.malcolmgin.com/oldblog/

Your new blog’s URL: https://blog.malcolmgin.com/

Note that you’re going to want to edit carefully, use a good search and replace editor (I use Atom – it doesn’t have the most sophisticated regular expressions, but it does the job), and when you do search/replace, start out with the most specific that’s applicable, and then go general. I’ll show you what I mean in a minute.

Here’s a rough game plan for searches and replaces that I used for the two more difficult migrations. If you want to be bold and just do a couple replacement runs, be my guest. It may not be worth the bother to be more careful and specific.

The quick way:

Find: wp_1a2b3c, Replace: wp_1z2y3w
The first, most important edit, to change the restore to the new blog’s database table name prefixes.

Find: http://www.malcolmgin.com/oldblog/, Replace: https://blog.malcolmgin.com/Be careful with this Optional approach. This is the quickest way to do it, but may blow away more subtle distinctions you want to keep. Alternately you can ignore this one and do replaces below instead, in order of decreasing specificity, to be more careful about it. Since you are completely migrating from the old to the new blog, it’s probably a safe assumption that it’s fine to replace all occurrences of the old URL with the new one. But think about it before proceeding. Note also we’re changing http:// to https:// because of the SSL Let’s Encrypt certificate.

The more careful approach:

Find: wp_1a2b3c, Replace: wp_1z2y3w
The first, most important edit, to change the restore to the new blog’s database table name prefixes.

Find: http://www.malcolmgin.com/oldblog/wp-admin/admin.php?p, Replace: https://blog.malcolmgin.com/wp-admin/admin.php?p
Do specific changes first, then gradually more and more broad strokes until all the old URLs are gone. Note also we’re changing http:// to https:// because of the SSL Let’s Encrypt certificate.

Find: http://www.malcolmgin.com/oldblog/wp-content/uploads/2, Replace: https://blog.malcolmgin.com/wp-content/uploads/2
This addresses all the media library entries, which are usually uploaded into subfolders of root folders named after the years in which they were uploaded. Since my oldest blog starts around 2009, just searching for the 2 is enough here. It would be rare but you might also search for “1” at the end too.

Find: http://www.malcolmgin.com/oldblog/, Replace: https://blog.malcolmgin.com/
After you have the more specific stuff out of the way, then do the least specific search/replace to clean things up.

The possibly too risky approach might also consist of finding old usernames and replacing them with new, though you may want to do this after restoring the database as a separate database edit.

Once you’re satisfied you’ve edited the MySQL Backup, save your working copy to a place where you’ll remember putting it.

Click the phpMyAdmin link next to the hostname you created in Step 3.2.1.

Log in with:

MySQL Hostname: db-blog.malcolmgin.com

Username: abcdefgh

Password: %4wh1*3zb@

Once you’re logged in, follow the official documentation on how to do the restore to this new blog database.

Now, like I said before, because you’ve restored a backup from the old blog, even though you did a bunch of searches and replaces, unless you also hand edited the user information, the logins and users from the old blog are now in effect, and you have to remake the new users if you want to move forward with the new information.

Because passwords are particularly difficult to work with by hand (because encrypted stuff isn’t easy to mess around with unless you’re very careful), I recommend redoing passwords through the WordPress layer of the interface, not by messing around with passwords in the database layer, in phpAdmin.

That said, you can edit the username information in phpMyAdmin. Not wanting to reinvent the wheel, here’s a reasonable article on how to change the Admin username from WPBeginner. The method I’m talking about here is Method #3 in this article.

Change the Admin username back to the new one:

While still logged into phpMyAdmin from step 11 above, click the plus sign next to the database name (e.g. mysqlblogwp).

In your local directories, navigate to the similar subfolder. e.g. ./wp-content/uploads/

Select all the files in the local directory (e.g. directories named by years, and .htaccess if any) and transfer them to your new blog’s uploads folder. This transfer may take a while to complete. There are a lot of files. For my four blogs, this step took about 5 minutes.

Try Logging In

Yay! Now, either the hard way or the easy way, your new blog should be up and running with the old content in the new site and URL.

Remember: If you had to do this migration the hard way and had to restore the database instead of using Export/Import, even though you changed your username in the restored database for the new site in Step 12, the password and any extras, like 2 factor authentication, will be the same as your admin password was on the old site.

Anyhow, log in with your new or old passwords, whichever is applicable, and see if the Dashboard loads up. It should. Also check your Media Library and make sure real pictures are showing up there. If you have an image-heavy theme, try the public URL (in this exercise, e.g. https://blog.malcolmgin.com/

Everything should be working by now, and you should just have to do cleanup tasks.

Cleanup Tasks

First, don’t break the web. Don’t move your old blog without leaving something behind to clean up afterwards. The best way to do this is to create a redirect. Dreamhost allows us to do this with .htaccess files. You create a file called, just ‘.htaccess’, and put a rewrite directive in there for your old blog, redirecting to the new. This is pretty simple on DreamHost.

Create a redirect directive at the old URL:

Using either an SFTP session or a SSH session (note on DreamHost you have to change the user associated with the domain to a SSH account before you can connect via SSH – Also see DreamHost’s docs on setting up a SSH connection), create a file or edit a file named ‘.htaccess’. It will be located at the root directory of the domain that your old blog is in.
e.g. for this example, the path to the .htaccess file is: /home/username/blog.malcolmgin.com/.haccess
OR when you log in with the username (from step 2.3), you’ll start at /home/username, so the path you have to further navigate to is just blog.malcolmgin.com/.htaccess

Now that you are editing the file, insert a line to redirect (from old to new) at the end of the file:

RedirectMatch 301 ^/oldblog/(.*)$ https://blog.malcolmgin.com/$1

Save your file and test it.

Open the root directory of the old blog in a browser by entering the URL, e.g.: http://www.malcolmgin.com/oldblog/ and watch as the browser redirects itself to https://blog.malcolmgin.com/

Open a random article on the new blog, e.g. https://blog.malcolmgin.com/testpost/, make sure it loads, copy the path to the post, e.g. /testpost/, and append it to the end of the old blog URL, e.g. http://www.malcolmgin.com/oldblog/testpost/. Take that old URL and use it in a browser, and watch as the browser redirects itself to https://blog.malcolmgin.com/testpost/

Congratulations! You have saved your corner of the web!

This is also a good time to finish the job of moving forward with new account names and passwords at the WordPress layer. Don’t forget to:

Change the admin account’s password to the new password (if you restored the database instead of importing your WordPress content).

Creating or recreating (if you had to do it the hard way) your non-admin accounts. You can create, e.g., a new Author account, and then delete the old, transferring the old’s content to the new Author.

There may be some leftover configurations for you to do of Plugins, e.g. both Akismet and Jetpack tend to require additional configuration. For both, be aware that if you are a hobbyist or a small time blogger, there are free options for both services. Dig around and you can find them.

Once you are sure of your migration and sure of your new site, and once you’ve created a Redirect in your .htaccess to point users to the new site, delete the WordPress install files and old database for the old site. You may want to do one final backup of the files and the database just to be sure. But do go ahead and remove the files and the database from being in service, on your web host filesystem and servers. This helps clean up any lingering security issues with the old site, which is why you migrated in the first place, right?

Let me know if you discover other good Cleanup Tasks. To be honest, I’ve spent all day writing this post, based on work I did yesterday and today. Despite working from notes and artifacts from that work, it’s quite possible I forgot something.