I'm running a property site where many properties are sold in apartment blocks.

Because of this what the content editors do is create a post/property with all the details and then use a duplicate post plugin to create the others.

Each time they duplicate a post/property they change the title to reflect the property number and maybe change a few bits of meta data E.G price.

What they forget to do is wipe out the slug and let a new one be generated from the title. Here is an example slug from the first property entered:


but then when they duplicate the slugs become:


But when they change the titles the slugs would be better like:


So my question:

How do I force the slug to be re-generated on post save, after they have updated the property title?

The slug for this CPT should always be auto generated, there is never a need to manually set it.

4 Answers 4


The easiest workaround could be:

function myplugin_update_slug( $data, $postarr ) {
    if ( ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
        $data['post_name'] = sanitize_title( $data['post_title'] );

    return $data;
add_filter( 'wp_insert_post_data', 'myplugin_update_slug', 99, 2 );
  • Does wp_insert_post_data get fired on post update too? The post has already been created by the duplication process. The slug needs changing once the post is updated with the correct title.
    – Scott
    May 22, 2012 at 11:01
  • NVM I found the answer to that comment: A filter hook called by the wp_insert_post function prior to inserting into or updating the database.
    – Scott
    May 22, 2012 at 11:02
  • Yes, it does. Whenever you create or update a post, this filter will be fired. May 22, 2012 at 11:14
  • 1
    I've attached to the if statement a check to make sure I'm on my post_type too.
    – Scott
    May 22, 2012 at 11:34

Also, run the slug from sanitize_title_with_dashes() through wp_unique_post_slug() to ensure that it's unique. It will automatically append '-2', '-3' etc. if it's needed.


Instead of replacing spaces you should use the build in function sanitize_title() which will take care of the replacing for you.

Like this:

sanitize_title( $post_title, $post->ID );

Also, you should use a unique slug. Which you can get with the function wp_unique_post_slug()

So putting it all together a solution might be:

function myplugin_update_slug( $data, $postarr ) {
    if ( ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
        $data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'] ), $postarr['ID'], $data['post_status'], $data['post_type'], $data['post_parent'] );

    return $data;
add_filter( 'wp_insert_post_data', 'myplugin_update_slug', 99, 2 );
  • Thanks for this! Your change to the accepted answer is very important. It should probably be added to it as an edit though.
    – eddiemoya
    Mar 3, 2014 at 6:01

Something I've had bookedmarked for a little while is the following (yet untested),

Source LINK

//add our action
add_action( 'save_post', 'my_save_post', 11, 2 );

function my_save_post($post_id, $post){

   //if it is just a revision don't worry about it
   if (wp_is_post_revision($post_id))
      return false;

   //if the post is an auto-draft we don't want to do anything either
   if($post->post_status != 'auto-draft' ){

       // unhook this function so it doesn't loop infinitely
       remove_action('save_post', 'my_save_post' );

      //this is where it happens -- update the post and change the post_name/slug to the post_title
      wp_update_post(array('ID' => $post_id, 'post_name' => str_replace(' ', '-', $_POST['post_title'])));

      //re-hook this function
      add_action('save_post', 'my_save_post' );
  • In that code surely the re-hook is not needed and even if it was it would need the priority and args set to 11 and 2? Also instead of str_replace() would sanitize_title_with_dashes() be more suitable?
    – Scott
    May 22, 2012 at 11:18
  • By calling wp_update_post function, you will call wp_insert_post function, which fires save_post action. You will get infinite loop. May 22, 2012 at 11:19
  • @EugeneManuilov - but in the code the hook is removed so not to loop infinitely...
    – Scott
    May 22, 2012 at 11:24
  • @Brady ah... yes I see. My fault. May 22, 2012 at 11:29
  • 1
    @EugeneManuilov If you down-voted this answer, please reverse your vote. The reputation points are not of my concern, however the fact that this remains a plausible answer is and as such down-voting this answer provides a disservice to potential readers by misinforming them that this function will loop infinitely when it will not. Also this snippet provides commentary through out which will give those who lack the understanding a better chance at seeing whats happening at each stage of the function. They can apply this knowledge and understanding to your snippet also.
    – Adam
    May 22, 2012 at 11:47

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.