A group that I joined created a wordpress site before I joined. they did not set up permalinks for the site. So when I took over maintaining the site, I been optimizing the site both for SEO and speed. So I am getting to the posts task, which contain links to other posts on the same site, but because permalinks were off they look like http://THE_SITE/index.php?post_id=432 within every post. I already enabled the %postname% permalink, so google is already updating its urls because of the 301's, but the problem is the posts still have the old default links.

So the question is: What is the best way to update the old links to the new permalinks

  • 2
    What you're asking is not an insignificant amount of work, and if you ask me, it's really not worth doing. The 301 redirects and canonical meta tags mean that no search engine will index the old URLs, they'll index the correct ones. There's only one real reason to do what you're asking: reduce load on your server (each link will result in two requests due to the redirect). If you still feel the need to move forward with it, I personally think it qualifies as a question "that require[s] professional hands-on involvement" as detailed in the FAQ May 29, 2013 at 1:17

1 Answer 1


Question interested me, so I took a shot. Not tested, so recommend backing up the wp_posts table before running (though you should do so regardless!)

As mentioned in the comments, don't forget to replace "domain.com" in the regular expression with your own.

require 'wp-load.php'; // Only needed if *not* a plugin.

function out_with_the_old( $match ) {
    global $links;

    if ( ! isset( $links[ $match[1] ] ) ) { // Cache to save subsequent hits. 
        $links[ $match[1] ] = get_permalink( $match[1] );
        clean_post_cache( $match[1] );

    return $links[ $match[1] ] ? $links[ $match[1] ] : $match[0]; // Just fallback to original URL if new permalink void (for whatever reason).

$links = array();
$posts = get_posts(
        'posts_per_page' => -1,
        'post_status'    => 'any',
        'post_type'      => 'any',
        'fields'         => 'ids', // Getting *everything* will most likely hit the memory limit.

// Loop over each post & replace all old instances of the permalink with the new one.
foreach ( $posts as $post_id ) {
    $save = array();
    $post = get_post( $post_id );

    foreach ( array( 'post_content', 'post_excerpt' ) as $field ) {
        // Replace "domain\.com" with your domain name, minus "www." (escape dots with a backslash).
        // The regex accommodates for various forms of the same URL, including SSL, no "www" & no index.php.
        $text = preg_replace_callback( "!https?://(?:www\.)?domain\.com/(?:index\.php)?\?(?:p|post_id)=([0-9]+)!", 'out_with_the_old', $post->$field );

        if ( $text !== $post->$field )
            $save[ $field ] = $text;

    if ( $save )
                'ID' => $post_id,

    clean_post_cache( $post ); // Otherwise we might soon hit a memory limit.

You could turn this into a plugin, but the cheap-n-easy way would be to dump into a script, FTP to the same directory as WordPress, then run in a browser.

  • This actually works perfectly. Very useful if someone starts a WordPress and never turns on permalinks in the beginning. Thanks a lot!
    – Joe Block
    May 30, 2013 at 4:38
  • 1
    I just had to change the query name so it catches both ?p and ?post_id
    – Joe Block
    May 30, 2013 at 5:04
  • @joebl86 Thanks for the heads up, answer updated. May 31, 2013 at 6:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.