How to Update Your WordPress Permalinks Without Causing Link Rot

If you are changing your permalinks structure differently than what is described below (e.g., going from name-based permalinks back to name and date-based permalinks), there is a WordPress plugin that can take care of you. If all you want to do is change from name and date-based permalinks to name-based permalinks, the method described below should be a bit faster, as the redirect is handled at the server level rather than at the PHP level.

This is a follow-up to my earlier post Seamless Permalink Updating. The problem I faced was that I wanted to change my WordPress permalink settings from being date and name based to something which was simply name based.

Why did I want to do this?

  1. Permalinks would be shorter; removing the date information results in URIs which are eleven characters shorter.
  2. Permalinks would not only be more readable, they would be more meaningful to humans; dates aren’t as important as the content the page itself contains. If presented with a list of permalinks from this site, users should have a good idea of what they’ll get when they click them. There’s no reason to muddy those waters by injecting date information into the mix, diluting the impact of the domain and post slug.
  3. The content part of the permalinks (domain name + post slug) would carry more impact in various search engines, without date information (that may or may not be recognized as dates by search engine algorithms) diluting the value of whatever keywords may be present.
  4. Yes, this is a blog, and yes information is posted chronologically; however, this isn’t a novel, and the material here need not be read in a chronological manner in order to understand it. Having date-based permalinks creates the illusion that the date the material was posted is more important than it really is. Besides, posts are displayed with the date anyway; why give it to the user twice when I’m betting the date included with the post is glanced over by most users.

Great reasons, I think, which should suffice for now. So I wanted to change my permalinks; what did I do about it?

WordPress makes the process ridiculously simple.

  1. Visit the Permalinks page of the administration panel (/wp-admin/options-permalink.php).
  2. Select the button labeled “Custom, specify below.”
  3. In the box labeled “Custom structure,” enter the text /%postname%/
  4. Submit the change via the “Update Permalink Structure »” button.
  5. Visit your site and verify that posts are not being given permalinks in the style example.com/post-name-here/. Beautiful, no?

Like I said, ridiculously easy!

But consider: What happens to all the links out there on the Internet to your blog which make use of the previous style of permalinks? Try it out for yourself; find an old permalink in your browser’s history that still makes use of “Date and name based” permalinks and visit it.

404! You broke the Internet! File not found! It’s the end of the world as you know it!

But don’t worry, you can feel fine about it because I have a solution, provided that you are using Apache as your server environment (chances are, you are) and mod_alias is enabled (I’m unsure how common it is). We’ll be fixing our problem using the RedirectMatch directive of mod_alias.

  1. Download your blog’s primary .htaccess file; most likely, it’ll be in the same directory as your blog’s wp-config.php file.
  2. Open it up, and find WordPress’ permalinks code. If you’ve never edited your .htaccess file before, it will most likely be the only code present. The code looks like this, give or take a line break:

    # BEGIN WordPress
    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    </IfModule>
    # END WordPress
  3. Before that WordPress code, you’ll want to add one of the following:
    • If your WordPress is installed in your domain’s root directory (e.g., example.com/wp-config.php), add this code, adjusting example.com to your proper domain name:

      RedirectMatch permanent ^/[0-9]{4}/[0-9]{2}/[0-9]{2}/([a-z0-9\-/]+) http://example.com/$1
    • If your WordPress is installed in a subdirectory of your domain (e.g., example.com/blog/wp-config.php), add this code, adjusting example.com/blog to your proper domain name and directory:

      RedirectMatch permanent /blog/[0-9]{4}/[0-9]{2}/[0-9]{2}/([a-z0-9\-/]+) http://example.com/blog/$1
  4. Save & upload the .htaccess file, and revisit one of your older date-based permalinks. If all went according to plan, you should be seamlessly redirected to your new name-based permalinks.

That’s all there is to it! If anyone can provide a better means, I’m open to it, but I’ve tested this out, and it works exactly as I want it to — posts work, paged posts work, pages work, and so on. I can’t promise this won’t break any WordPress plugins, so be prepared to give up this method (which is as easy as deleting whatever you add to .htaccess and switching your permalink structure back).

For the curious, this is what the code above does, piece by piece:

RedirectMatch
Begins the call to Apache’s mod_alias module.
permanent
Sends the client (i.e., the user, whether human or robot) instruction that this redirect is a permanent redirect and that the old address is invalid and should be from now on replaced with the new.
^/[0-9]{4}
The address which we’re trying to match begins with a four-digit year stamp, so we first look for a slash followed by four numbers.
/[0-9]{2}
The address will next have a two-digit month stamp, so we check for another slash followed by two numbers.
/[0-9]{2}
The address will next have a two-digit day stamp, so we check for yet another slash followed again by two numbers.
/([a-z0-9\-/]*)
The address will then have the post slug, which can consist of a variety of lowercase letters, numbers, and dashes. So we look for any number of those. A post slug may include additional information after it, such as a page number on paged posts, so we include any number of slashes in our search as well. It’s enclosed in parentheses so we can capture that data and use it in the next section.
http://example.com/$1
This is the location of where we want to redirect to. The $1 at the end will be replaced by whatever was matched by the code in parentheses in the previous statement.

Edit: Whoops! Using the above RedirectMatch code prevented viewing of day archives (e.g., /2008/01/01/); swapping the asterisk (*) out for a plus sign (+) resolved the issue and allows day archives to be viewed. The code above has been corrected.

58 thoughts on “How to Update Your WordPress Permalinks Without Causing Link Rot”

  1. Excellent post, Rick.

    I am seriously (re-)considering removing the date portion of my permalinks. One of the last (logical) reasons I can think of to avoid this change involves the ability to display posts via year, month, and day views. To what extent are the archive pages working with this method in place? And finally, have you noticed any conflicts with plugins, etc.?

    Also, you may be amused to know that in an attempt to solve this mystery, I had discovered the same htaccess technique and even wrote a (remarkably similar) article explaining the implementation process.. I was ready to roll with it, but by the time I had finished my work week and returned to the computer, you had already devised a solution and published an article — doh!

  2. Chris — Saw that linked to earlier from Weblog Tools Collection. I only read the excerpted quote then, but followed your link and read it in full.

    He’s absolutely right when he implies that permalink design is very site specific.

    However, it would be interesting to tinker around and see how much pull a permalink has on how Google indexes — for instance, if the above post had a permalink which referenced Britney Spears, Paris Hilton, and so on, how long would it take for traffic to show up looking for that kinda stuff, if at all?

    It’d make for an interesting experiment, I think!

  3. Lol, I’ll leave the tinkering and experimenting to you :-) I had a post and poll about credit cards a while back, and I noticed a big change in amount and type of spam…

  4. Great post, worked like a charm for converting my wordpress permalinks from the default ?p=\d+ format to the /%postname%/ format. Thanks!

  5. ryan woodrum & amy — I’m glad both of you were able to benefit from this guide! If you have any problems related to permalinks as a result of following any of the above advice, just drop a comment, and I’ll do my best to sort out your issue(s)!

  6. We want to thank you for this simple, easy-to-follow instructions on the redirection. We’ve been trying to do this for weeks now and, because of your post, I just fixed it in 2 minutes! We’re bookmarking this!! Thanks so much.

    Amy and Jonny – http://www.weareneverfull.com

  7. Could you post the contents of the modified .htaccess you are using? You can paste it onto this site [ http://pastebin.com/ ] and give me the link to the resulting code page.

    A Server Configuration problem could be caused by any number of things, unfortunately, but I’ll take a look for ya!

  8. Hi, I tried the htaccess rule you suggest, of course I change example.com to my website, but all I get is an error page about server configuration and additional 500 error. My WP installation is in my domain’s root. Any Idea?

    Thanks you :-)

  9. James: Try adding the lines I added — they should be highlighted:
    http://pastebin.com/f3d55d0a9

    If the server error goes away and the redirecting does not work, then the cause is because Mod_Alias is not configured on your server and a solution using Mod_Rewrite will need to be used.

    For more information on Mod_Alias:
    http://httpd.apache.org/docs/1.3/mod/mod_alias.html

    Jeff over at Perishable Press presents the Mod_Rewrite rules if you want to try them out.

  10. That was fast :-)
    Right now I set up a cpanel redirect but that’s for a couple of old posts. I haven’t actually change the permalink structure in WP, I just change some posts date and since then I’m seeing few errors caused by visitors coming from search engines.
    Anyway, here’s my htaccess http://pastebin.com/d3ad3a557

  11. Thank you so much for the information. The plugin that many people use doesn’t work for converting from default pages, but luckily I stumbled upon your solution, and it works perfectly. Thanks again for your helpful article.

  12. Hi Rick,

    I had the month and name structure for the permalinks of my blog. Recently, due to other problems, I deleted the whole WP installation and did a new clean one, then I re-imported all my posts and now I’ve set directly the permalink structure to /%postname%/.

    So now I’ve the problem that in the old blogs I’ve still internal links with the old “month and name” permalink structure. I thought to fix the problem with your redirection, but it doesn’t work: I get an 404 error.

    Take a look at my .htaccess file http://pastebin.com/m15779b1e and please help me to find where I fail.

    Thanks in advance & ciao

    alexander

  13. Alexander – StrategieVincenti: Troubleshooting HTAccess problems is not something at which I’m at all skilled. If my solution above doesn’t work for you, try out this one from a friend of mine. We were both working at solving the problem of shortening permalinks at the same time. I’m pretty sure his solution will work in a wider variety of cases as it uses the same technology as WordPress permalinks. My solution uses a different Apache module than the WordPress rewrite rules do; it may not be present or enabled on all systems.

  14. Hi Rick,

    very interesting article.

    Over the weekend, I actualy did a double change. I switched from a windows based server to the Apache version, to get rid of the stupid index.php

    And on top of that, I wanted to get rid of the day in the url, but keep the year and month.

    To do all this, I used the tool that you mention and that may have a security breach. So I’m certainly willing to give your suggestion a go, but how would that string look like in my case?

    Original url:
    http://www.domain.com/index.php/%year%/%monthnum%/%day%/%postname%/

    Current URL:
    http://www.domain.com/%year%/%monthnum%/%postname%/

    Beside the already mentioned plugin, I also use the Objection Redirection plugin (http://www.biggnuts.com/objection-redirection-wordpress-plugin/). I read about this in an article on a Permalink Change by DoshDosh. Does this still make sense if I apply the .htaccess tool?

    Kind regards,
    Max

  15. I just did a hellish move from an extremely outdated Movable Type installation to WordPress. All I wanted was to redirect my old permalink format:
    /blog/archives/123456.php

    to my new format:
    /blog/archives/123456

    I Googled for hours and tried all sorts of redirect rules, and nothing worked until your Redirect Match tip. THANK YOU!

    Latest from Michelle: Mouldy Speeches

  16. Awesome, exactly what I was looking for. Thank you so much for your fantastically clear information, your help pointing me to this article and your willingness to share additional outside links for further insight. I ended up having to go with the mod-rewrite version, but it works perfectly!

  17. Awesome! Thanks a lot dude. You made my day, after searching up and down in the internet, trying few plugins which needs more tweaking works.. this one piece of code save the day!!!!!!!!!!!!!! :D

  18. Hi Rick – just wanted to say a HUGE thank you for your clear and concise instructions. They worked a treat when I changed my permalink structure and the search engines love it!

    Thanks again – you’re a genius :-)

  19. You rock, Rick! But you already knew that, right? :)

    I switched around my permalink structure last night–courtesy of http://www.keenerliving.com/leaving-the-dates-out-of-your-urls-blogging-tip/ — but the RedirectMatch line wasn’t working correctly.

    After emailing back and forth with Bruce, I had it down to

    RedirectMatch 301 /([0-9]+)/([0-9]+)/(.*)$ http://ariwriter.com/$3

    That solved the posts, but created a conflict with date-based archive pages and uploaded images in date-based wp-content folders. One link led to another–and I came across this. I now have:

    RedirectMatch 301 ^/[0-9]{4}/[0-9]{2}/([a-z0-9-/]+) http://ariwriter.com/$1 and it works great!

    Better to use htaccess than plugins I figure; cleaner!

  20. When you say add it BEFORE the wordpress code, do you mean before the line that starts “# BEGIN WordPress” or just after that??

    Thanks

  21. I’ve just removed the date from my permalinks. Used to have the year and month in the URL but now changed it to just the post title. Haven’t seen an effect for SEO yet. I’m hoping that removing the date will increase click through as people don’t want to click on a link where they know the content is a couple months old.

  22. Thanks for this great info. I’m just trying to figure out a few details before proceeding with this, and hope you can help. My current permalink structure is (setup to mimic Blogger, since it was originally there):
    /%year%/%monthnum%/%postname%.html
    I want it to be:
    /%post_id%/%postname%/
    because some articles (like this one http://wpmu.org/the-best-wordpress-permalink-structure-for-scaling-performance-and-seo/ ) suggest it’s bad for long-term scaleable performance to go directly to postname.

    So based on your article, I assume I’ll just remove the “/[0 – 9]{2}” part, because I’m not using the day in the current permalink? Then do I also need to do something other than the “$1” on the end, since it will now have the post ID before the name/slug? Thanks.

    1. I haven’t tested this, but try this out:

      RedirectMatch permanent ^/([0-9]{4})/[0-9]{2}/[0-9]{2}/([a-z0-9-/]+).html http://example.com/$1/$2/

      This assumes a /%year%/%postname%/ format. The problem with using the post number is that your original permalinks don’t have that information, so i’m unsure how to map it via regex to the new permalinks. Using the year alleviates the issue you mentioned, though.

      Based on reports i’ve seen, the inefficiency with /%postname%/ permalinks is only an issue if you have many, many pages (posts are irrelevant). By adding a number (year, post number, whatever) to the permalink, WordPress more easily distinguishes between posts & pages and so the inefficiency is no longer there.

      Actually, i seem to recall an update in WordPress that fixed that issue altogether. Still, i chose to add the year to my permalinks here out of anticipation of some day having lots of pages (which may or may not actually happen).

  23. Thanks for the response Rick. I was wanting to use this plugin: http://wordpress.org/extend/plugins/old-post-promoter/ to promote old posts (most of this site’s content is not time-sensitive). It won’t work if you use any dates in your permalinks.

    Now that you say that about it only being related to pages (not posts) and possibly fixed in a recent version of WP, I seem to remember at least the latter, as well. I’ll just go with the slug/post name, no post number or anything. But I will add the “.html” on the end of the first part, to make it work correctly.

    So I don’t remove the “/[0-9]{2}” even though my current URL doesn’t have the day in it?

  24. No, you’re right about that: remove the unnecessary pointer to the day!

    Also, WordPress does a pretty good job of getting users to the correct location after you change your permalinks. When i updated permalinks here to include the year, i didn’t have to do anything special to get old links to point to the correct place. It just worked. I’m not sure if it’ll still work correctly with the “.html” bit of yours, however, so try it to see, and if it does work, you won’t need to modify your .htaccess file.

  25. Also, it seems like you’d be able to promote old posts by setting their status to sticky temporarily rather than having their post time adjusted. That’ll make them reappear in feeds, which could annoy longtime subscribers. Probably not, though. Just thinking allowed. There’s just gotta be a better way to accomplish promoting old posts than by modifying post time, which would lead to the situation of having post content from 2011 that has comments on it dating from 2009 or so… Not a huge deal, but i’m just obsessive enough to be bothered by it. :P

  26. Agreed. I’d prefer another method without rewriting the date, but haven’t been able to find anything. Since most of my RSS subscribers are new, but a decent amount of content is not (revived somebody else’s old blogger blog), I actually want it to post and show up in the RSS as well. This is the best solution I’ve found, so I’m going to give it a try.

    Thanks much for your help.

  27. I added this:
    RedirectMatch permanent ^/[0-9]{4}/[0-9]{2}/([a-z0-9\-/]+)\.html http://mormonlifehacker.com/$1

    And it worked perfectly! I decided to go without the post number. I installed that plugin and we’ll see how it works….

  28. Hello i wanted to change my perma link but not worked the redirect.

    my structure is /%year%/%monthnum%/%postname%.html and wanted to change to /%postname%/

    the redirect should still be RedirectMatch permanent ^/[0-9]{4}/[0-9]{2}/[0-9]{2}/([a-z0-9\-/]+) http://example.com/$1

    or i am doing it wrong?

    1. You would need to use a modified redirect, which takes into account your unique structure (####/##/example.html). I’m not sure entirely on how to account for the “.html” bit.

  29. old permalinks:

    /%category%/%postname%/

    and I changed it to: (new permalinks)

    /%postname%/

    How to Redirect old links after change of Permalinks? wiht HTACCESS?

    thanks

    1. You could try adding this to your .htaccess file, though I haven’t tested it.

      RedirectMatch permanent ^/[a-z]+/([a-z0-9-/]+) http://example.com/$1

      Be sure to update the example domain name to your own.

    1. Not sure why — I’m by no means a regular expressions guru. If you could share with me a few examples of addresses which you want to work but which are redirecting to the home page, that could be useful.

      Til this, here’s a slight refinement which would account for categories with multi-word names:

      RedirectMatch permanent ^/[a-z-]+/([a-z0-9-/]+) http://example.com/$1
  30. I’m working on it. As I said, not a regex expert, and so there’s a lot of trial and error on my part trying to find the right syntax. I’ll try to have an answer posted sometime today for you.

  31. The category one is going to be problematic as its format (/category/category-name/) exactly matches your old post permalinks (/category/post-name/).

    Are you using the latest version of WordPress? If so, changing your permalink structure should be handled gracefully by WordPress at this point, without the above custom code. I may have imagined that, though, so you might look into the Permalink Redirect plugin to accomplish what you want.

    Aside from that option, from the looks of it, in order to prevent your categories from redirecting if this is necessary, you’re going to need a rather lengthy block of code which tests against every one of your category addresses, and if all of them fail, then it’ll redirect the post to the new post permalink. This is going to be pretty inefficient as these tests will need to be made on every page load.

    1. I’m assuming the page that is redirecting is index.html? If that’s the case, then the redirect is working properly. Try these two lines together:

      RedirectMatch 301 ^/index.html$ http://www.hostcook.com/
      RedirectMatch 301 ^/([^/]+).html$ http://www.hostcook.com/$1
  32. Hi Rick,
    I am new to WordPress and have recently built a website, but the Permalinks were unfortunately an afterthought. Now I am stuck with the old structure which is /%123?%/ i.e. each page is just a number. I want to change my permalinks so that they match the title of the page. I tried changing this in the WordPress options but when I then visit my site my browser just says page not found. Can you help buddy?
    Cheers,
    Charlie

  33. Hi Rick,
    I am new to WordPress and have built my site but unfortunately I didn’t give any thought to Permalinks… until now! Unfortunately however when I change the options in WP from the old structure /%123?%/ i.e. just a page number a new one which matches the title of the page, when I then visit the site the browser just tells me “Page not found.” Can you help buddy?
    Cheers,
    Chaz

  34. Hello,

    My site was running with permalink structure /%category%/%year%/%monthnum%/%day%/%postname%/ earlier. Now I changed the structure to /%category%/%postname%/ but the Old Url’s are not properly redirecting back to new one. I tried the redirect rule RedirectMatch 301 ^/blog/([^/]+)/([0-9]{4})/([0-9]{2})/([0-9]{2})/([^/]+)/$ http://www.mysite.com/blog/$5.

    Can any one please help in writing a proper redirect rule.

    Thanks

  35. I had this permalink structure /%category%/%year%/%monthnum%/%day%/%postname%/ and I changed it to /%postname%/. I added
    RedirectMatch permanent ^/([a-z0-9\-/]+)/[0-9]{4}/[0-9]{2}/([a-z0-9\-/]+) http://www.monkshouts.org/$2 line to htaccess, all works fine and all the earlier posts with older permalink are redirected to new permalink structure but many of the images in the home page are broken. could you suggest how to fix this?

  36. Hi Rick, this is a really cool article! One question: I’m using seo yoast, I published a page and I want to change the permalink (just to add a more friendly keyword). How can I do it without to get a 404 error? Thank you so much!

Leave a Reply to James Cancel Reply

Your email address will not be published. Required fields are marked *

Use your Gravatar-enabled email address while commenting to automatically enhance your comment with some of Gravatar's open profile data.

Comments must be made in accordance with the comment policy. This site uses Akismet to reduce spam; learn how your comment data is processed.

You may use Markdown to format your comments; additionally, these HTML tags and attributes may be used: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.

the Rick Beckman archive
Scroll to Top