maggienelson.com | more blog archives

From MovableType to WordPress in 301 Easy Steps

2009-01-25

I'm finally caving in and switching to WordPress.  I tried it long long time ago and was annoyed by some of its code (it's not always pretty) and the fact that pages are never cached - there are a lot of on-demand database requests.  But it's been a while and things seem to have improved - some within WordPress itself, but others due to the help of the community which has provided tons of plugins that can help WordPress get around some of its problems.

The move from MovableType to WordPress was easy.  WordPress has import functionality that plays very nicely with MovableType's exported files.  Yay!

What got me, though, is how to handle making sure that visitors to my old site would know to come to the new site.  Long time ago, I'd think to just take everything down and have a page saying "stuff has moved, deal with it, here's the new URL", but that's not really very user friendly.  Michael Bloch points out here the not-so-good approaches for moving your site and recommends using the HTTP status code 301: Moved Permanently.  This will make everything magially work: users will be redirected properly and search engines will know that the resources have moved and start looking for them in the new places.

When using 301, you have two choices:

I went with #2, because it just hurts me so to hard-code too many things.

The first biggie when using WordPress is that you're probably going to set up pretty URLs for permalinks to your entries (this is done under Settings).  If you do, your .htaccess file will already contain:

RewriteEngine On

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . /index.php [L]

This is the .htaccess on the new domain.  Keep this in mind because these rules may affect with any other rules you add if you don't chain them properly.

Ok, the next step was handling the old site: www.objectivelyoriented.com.  This was easy - I added a .htaccess file in the web root that contains:

Redirect 301 / http://maggienelson.com/

This means that any URL from objectivelyoriented.com will be sent to the same URL on maggienelson.com.

objectivelyoriented.com -> maggienelson.com

objectivelyoriented.com/maggie-is-totally-awesome/ -> maggienelson.com/maggie-is-totally-awesome/

objectivelyoriented.com/i_like_php_so_much.html -> maggienelson.com/i_like_php_so_much.html

This is pretty lenient, but we'll remove some of the leniency a little later on with additional, more explicit rules.

Now, the problem here is that while MovableType and WordPress are both blogging applications, they handle their URLs differently, even though they both base the pretty URLs on the title of each entry and the month and year when the entry was published.

In MovableType, URLs are prettified like this:

/YYYY/MM/words_in_the_title_of_entry.html

MovableType will use only up to 6 words of the title of the entry and will truncate the last word if necessary to keep the length of the URL not too crazy (I admit, I was too lazy to actually check the exact limit).

In WordPress, URLs are prettiefied like this:

/YYYY/MM/words-in-the-title-of-entry/

WordPress does not have a limit on how many words from the title it will use.

Please note that in WordPress, you have freedom to change the prettifcation patterns - mine just happen to also have the month and day.  If you remove them, you will have to change the RewriteRules slightly.  But if you're playing with URL prettification, then I'm assuming that you care enough to figure out what you're doing.

Moving on.  As you can see, the URLs in MovableType and WordPress are pretty similar.  Both strip any non-alphanumeric characters from the URL and lowercase the remaining string.  The big difference is the use of underscores in MovableType and hyphens in WordPress.  MovableType also ends all URLs in ".html", which we'll need to strip for MovableType.

First, I handled the underscore-to-hyphen transformation along with the stripping of .html, but only for URLs that contain up to 5 words in the title of the entry.  Any of these URLs on the old site will be redirected to the equivalent URL on the new site - so you will see the entry you were after.

RewriteRule (200[6-9])/([0-9][0-9])/([a-z]+)_([a-z]+).html$ /$1/$2/$3-$4/ [R=301,L]

RewriteRule (200[6-9])/([0-9][0-9])/([a-z]+)_([a-z]+)_([a-z]+).html$ /$1/$2/$3-$4-$5/ [R=301,L]

RewriteRule (200[6-9])/([0-9][0-9])/([a-z]+)_([a-z]+)_([a-z]+)_([a-z]+).html$ /$1/$2/$3-$4-$5-$6/ [R=301,L]

RewriteRule (200[6-9])/([0-9][0-9])/([a-z]+)_([a-z]+)_([a-z]+)_([a-z]+)_([a-z]+).html$ /$1/$2/$3-$4-$5-$6-$7/ [R=301,L]

RewriteRule (200[6-9])/([0-9][0-9])/([a-z]+)_([a-z]+)_([a-z]+)_([a-z]+)_([a-z]+)_([a-z]+).html$ /$1/$2/$3-$4-$5-$6-$7-$8/ [R=301,L]

Yes, this was kind of ugly, but as far as I know, there is not way to do a search and replace on URLs when rewriting them (if you know of a way, I'm all ears!) Note that this goes before the rewrite conditions and rules for the WordPress URL prettification.

Then, I handled all URLs that have 6 or more words.  These will not be redirected to the equivalent entry, but instead, simply to the archive page for the year and month when this entry was published.  This works for me as my blog was not yet chockful of entries, so each per-year-per-month archive contains only few entries.  This might not work for you, but the choice I made here was between how long it would take and the payoff.  Basically, "close enough" was the result.

RewriteRule (200[6-9])/([0-9][0-9])/([a-z0-9_]+).html$ /$1/$2/ [R=301,L]

The last thing of note is that MovableType provides some search functionality, which is accessed at /mt/mt-search.cgi?[a bunch of search params here].  As the search engine is different withing WordPress, I did not bother to transform the search parameters and instead I'm simply moving all old search requests to the new search form, like this:

RewriteCond %{QUERY_STRING} .

RewriteRule mt/mt-(.+) /#searchform? [R=301,L,NE]

NE means "no escape" and is necessary in this case: if you omit it, # will become URL encoded.

This also takes care of any remaining MovableType native files - if you're trying to find some admin functionality or anything else, you're bounced to the search form on the new site as there aren't really equivalents for them on the new blag.

Last but not least is handling the old blogs feeds. The MovableType installation I had provided these feeds-related files: atom.xml, index.xml, index.rdf, rsd.xml.  I have not found the equivalent of these in WordPress, so I'm simply bouncing all of those to /feed/

RewriteRule atom.xml /feed [R=301,L]

RewriteRule index.xml /feed [R=301,L]

RewriteRule index.rdf /feed [R=301,L]

RewriteRule rsd.xml /feed [R=301,L]

That about covers it. As you can see, a lot of my choices were not extremely well researched and many of them were done on a hunch about MovableType's and WordPress's behaviors, but I'm really enjoying this part of MovableType: it works even if it's not perfect.

For reference, here's the two .htaccess files I created: on the old site and on the new site.

Comments (6)

2009-01-25 18:59:39 Greg Beaver said:

Hi Maggie, You can use a 404 trick to do a search/replace on a URL at maggienelson.com, and use the 301 header inside the 404 script. Thus, http://maggienelson.com/2006/12/some-trunca.html would cause a 404, triggering your 404 script (let's pretend it's 404.php. This script parses out "some-trunca" and searches blog titles, then sends the 301 header. You could also set up a table mapping blog title to new title or just create a static array from the titles on the old server and put it in your script. Happy moving :) Greg

2009-01-25 19:02:24 Chris Shiflett said:

Your feed redirects properly, which is the most important URL to maintain for a blog. However, it currently redirects twice because of your "broad blanket rules" approach. It might be worth making an exception for that one case.

2009-01-25 19:09:55 maggie said:

Oh, hmm... Good point - looking into it now!

2009-01-26 06:57:09 Dagfinn Reiersøl said:

Interesting. I happen to be working on the same thing, except my old platform is LifeType. I've generated a list of redirects by first pulling out all necessary ingredients from both databases using one SQL join and then performing a series of substitues in Vim. I looks like I need only one manual change in addition to get all the posts redirected correctly.

2009-01-27 09:19:31 Brandon Savage said:

Thanks so much for your tutorial! I'm doing this exact process (from a custom solution) and I really appreciate that you took the time to write this out. Thanks!

2009-01-26 13:32:54 Karl Katzke said:

Oh, that's interesting. Thanks for posting all that mangling. One note of caution: Various plugins will overwrite or alter some of the rewrite rules if you allow them to. Keep a backup of your final file!