I'm looking for a plug-in or bit of php that will go through about 300 posts that use hyperlinks in the old p format:

<a href="http://mysite.com/?p=3592">

and automatically replace them with their %postname% pretty permalink. Eg:

<a href="http://mysite.com/my-post-about-whatever">

For whatever reason, these posts don't auto-magically do this when we changed Permalink structure so we're currently going through one by one. Thousands of links. I -think- the problem is that we originally imported them from static HTML pages.

I'm wondering if the fact that they don't automatically re-write is because we're using a Custom Post Type?

OR, is there a problem with the way we set up the taxonomy that prevents the automatic rewrite? If so, can someone provide some suggestions. Here's our taxonomy for this Custom Post Type (docs)

//helpdocs taxonomy
add_action( 'init', 'create_docs_post_type' );
function create_docs_post_type() {
$labels = array(
    'name' => __( 'Documentation' ),
    'singular_name' => __( 'Documentation' )
    $args = array(
    'labels' => $labels,
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true,
    'show_in_menu' => true,
    'query_var' => true,
    'rewrite' => true,
    'capability_type' => 'page',
    'has_archive' => true,
    'hierarchical' => true,
    'menu_position' => 50,
    'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt',
    'comments',     'custom-fields', 'revisions','page-attributes','post-formats' )

register_post_type( 'docs', $args);
function docs_init() {
// create a new taxonomy
        'label' => __( 'Documentation_Tax' ),
        'sort' => true,
        'args' => array( 'orderby' => 'term_order' ),
    'hierarchical' => true,
        'rewrite' => array( 'slug' => 'docs' )

add_action( 'init', 'docs_init' );



  • Note that changing permalink structure in no way assumes WP will update links anywhere in content. But old links should still work and redirect to new links, does that not happen? Is is that much of an issue if links are completely functional?
    – Rarst
    Nov 5, 2013 at 18:56
  • Yeah, we have other content writers who demand to use the pretty permalinks. So we need to find a way to automatically update all the '?p=3345' to the pretty permalink. So... are there plug-ins that will do that?
    – jchwebdev
    Nov 5, 2013 at 19:02

1 Answer 1


You can cycle your posts, extract urls and then look at the query vars in them to create the proper link, an finally replace old links with new in post_content.

This kind of work should be runned once, so I suggest to create a plugin and run this function on deactivation.

I also created an helper function to extract urls from post content. WP 3.7 comes with a core function wp_extract_urls, however this function return url without query vars, so is useless for our scope.

 * Plugin Name: Url Replacer
 * Author: G.M.

register_deactivation_hook( __FILE__, 'gm_replace_my_old_urls' );

function my_extract_urls( $content ) {
  preg_match_all("#<a.*?href=[\"\'](http[s]*://[^>\"]*?)[\"\'][^>]*?>(.*?)</a>#si", $content, $urls);  
  $urls = array_unique( array_map( 'html_entity_decode', $urls[1] ) );
  return array_values( $urls );

function gm_replace_my_old_urls() {
  $query = new WP_Query( array('posts_per_page' => -1) );
  if ( empty($query->posts) ) return;
  foreach ( $query->posts as $post ) {
    $urls = my_extract_urls($post->post_content); 
    if ( empty($urls) ) continue;
    $new = array();
    $old = array();
    foreach ( $urls as $url ) {
      if ( ! substr_count( $url, home_url() ) ) continue; // external url
      $url_array = explode( '?', $url );
      $qs = array();
      if ( count($url_array) == 2 ) parse_str( $url_array[1], $qs );
      if ( ! isset($qs['p']) && ! isset($qs['page_id']) ) continue;
      $id = isset($qs['p']) ? $qs['p'] : $qs['page_id'];
      $link = get_permalink($id);
      if ( ! $link ) continue;
      $old[] = $url;
      $new[] = $link;
    if ( ! empty($new) ) {
      $post = (array)$post;
      $post['post_content'] = str_replace($old, $new, $post['post_content']);

Save this code in a file and save in plugin folder. After that go in your dashboard and activate it. Nothing should happen. Now disable the plugin. It should take some seconds when plugin is deactivated go to check your posts...

  • Makes sense. Thanks for taking the time. I'll run some tests and follow up with my results. Many thanks!
    – jchwebdev
    Nov 5, 2013 at 23:56
  • 1000 thanks for this. With a few tweaks, this is something we will use a LOT as we have a ton of old documents in HTML to import. I'm kinda surprised no one has done a proper plug-in for this. We can't be the only ones with lots of legacy content to update in this way. Best ---JC
    – jchwebdev
    Nov 6, 2013 at 19:24

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.