301 Redirects When Migrating WordPress Sites

Recently our team executed a 301 strategy on a site of about 500 pages to preserve SEO and inbound links. Of the eight sites that made up this multisite install only two were in scope, including the main site, which made the database migration interesting — but that's a topic for another post.

What's this about?

On larger sites, determining what links need redirects is usually more challenging than creating the redirects themselves. This post is about determining what needs redirection and then creating 301 redirects via file upload to a plugin. Below I outline a process for extracting url listings from sites, importing them to spreadsheets, exporting 301 mappings to a csv file and uploading them into a WordPress plugin (not htaccess) to create permanent 301 redirects. It's not a Swiss Army Knife mother-of-all-answers-to-every-migration scenario. (sorry). But for those new to the process or needing a refresher, it should give some insights into how to identify what needs to be redirected and tools and tips for streamlining the process.

Who's it for?

This post is for you if you'll be the one implementing — or managing the implementation of — 301s on your WordPress project. If you're implementing, you should be comfortable creating a php file with cut and pasted code, sFTPing a file and using a spreadsheet. You will need admin access to the legacy and new WordPress sites that are the subject of the 301 redirects. It assumes your WordPress sites are self hosted. If your site(s) are fully hosted on WordPress.com, you will not be able to FTP or run custom php files which means this post doesn't really work for you.

Determining what needs 301 redirects

First thing is to create a listing of all the urls from the old site — and also from the new one — so they can be compared. Where the old url is different or doesn't exist on the new site, you'll need to define the source and target in your redirection plugin. My favorite plugin for redirects is Redirection by John Godley. Eggplant 301 Redirects is another. Search the WordPress plugin repository if you'd like to consider others.

Listing all URLs and Titles on your WordPress sites

Your project is no doubt different from the one that inspired this post. Still yours is likely similar in ways that still make this relevant. In our project, many of the page and post names in legacy had changed or didn't exist in the new development and we needed a way to create a list of all urls and post/page titles.

I found the code we needed in a pastbin which worked great and allowed easy creation of a csv file.

Below is the process we used for creating url listings of the relevant sites, importing the listings to spreadsheets, exporting our 301 mappings to a csv file and uploading the 301s into the Redirection plugin in WordPress.

1. Create a program to list all site urls

Created a php doc with the following code, named it listurls.php, and placed it in the root directory of both the legacy and new sites.


/* create a list of post titles and urls */

include "wp-load.php";

/*you have 2 options in the line below
staus= publish will return posts and pages with type, URL and title
status= any will return in addition any attachments such as images
in that post immediately after in the list with URL and file name  */
$posts = new WP_Query('post_type=any&posts_per_page=-1&post_status=publish');
$posts = $posts->posts;

foreach($posts as $post) {
    switch ($post->post_type) {
        case 'revision':
        case 'nav_menu_item':
        case 'page':
            $permalink = get_page_link($post->ID);
        case 'post':
            $permalink = get_permalink($post->ID);
        case 'attachment':
            $permalink = get_attachment_link($post->ID);
            $permalink = get_post_permalink($post->ID);
    echo "\n{$post->post_type}\t{$permalink}\t{$post->post_title}";

2. Run the list url program

In a browser, we entered the path to our php file. (e.g. www.legacysite.com/listurls.php). This listed all page and post urls to the screen. We then copy/pasted this list into a text editor and saved it as a csv (legacy-urls.csv). We repeated this step for the new site, saving it as a csv file like new-urls.csv.

3. Import urls into a spreadsheet

We imported each file into a spreadsheet (Google Sheets) as a TAB delimited file. At this point we had one spreadsheet of legacy urls/page titles, and another for new site urls/page titles.

4. Manually match urls and develop a list of redirects

Now that we had complete lists of legacy and new urls, we attempted to match them. We created a new sheet called redirections. Whenever a legacy URL had a match* in the new site, we ignored it. Where there wasn't a match, we placed the legacy url in column one of the redirections spreadsheet. In column two, we entered the url that the legacy page or post should redirect to. Even for a small site of 500 pages, this can be a tedious manual task but we didn't really see any short cut around that.

*I'm assuming you know when two urls match for purposes of a redirection. E.g. if legacy url is www.websitename.com/about-us/ and the new url in the spreadsheet is www.websitename-dev.com/about-us/ and www.websitename-dev.com/about-us/ will become www.websitename.com/about-us/ after the database migration, that's a match. If legacy is www.oldname.com and the new site is www.newname.com, and you're going to redirect www.oldname.com to www.newname.com via an A record, CNAME or other technique, again there's a match. But if www.websitename.com/homeblog/post-a/ will become www.websitename.com/blog/post-a/, it's a no-match requiring a redirect.

5. Export the spreadsheet to a csv file

Once we completed the redirections mapping, we exported the redirections spreadsheet to a csv file.

6. Import the csv file via the plugin

Next, we imported the redirections.csv into the Redirection plugin. Do this within the WP Dashboard by going TOOLS > REDIRECTION > OPTIONS > (scroll down to) IMPORT and select and upload your csv file. If you're not using the Redirection plugin, refer to your plugin's docs to see how you might do this.

Again, we used the Redirection plugin and it had no documentation on how to structure the csv file. By trial and error we found that legacy urls needed to be in column 1 and new (aka target) urls in column 2 and that the import function does not populate the optional page title field in the redirection record no matter how we formated the csv file. This is why you should not include title columns in the file you are importing. (note: I submitted a suggestion to the developer of Redirection to add title as an import option, so there's a hope that that will be supported by the time you read this.)

7. Review, test, monitor

Once the import is complete, review your redirections to make sure everything looks right within the plugin. (Tedious but helpful) Test the redirections in the browser too, unless you're thinking what could possibly go wrong? (plenty). In our case a couple of important subscription pages ended up in redirect loops due to some bad redirection definitions. This was easily rectified and caused no issues because we found them in testing before anyone else did. (Again, tedious but helpful) Also keep a close watch on your 404 logs in the days following go-live, to address any unforeseen issues.

That's it. Pencils down. Feet up. You're done!

8. Giving Credit Where Credit's Due

This process was lifted from the support post here. Special thanks to mgason, tigtog and Esmi for their contributions to the support post and who in effect engendered this tut and deserve the credit if you find it helpful in any way.

Leave a Reply