I'm creating a system, where some custom post drafts are being prepared, and after certain actions taken by moderators it is being automatically published.

So, first I create post draft like that:

$newpost = array(
    'post_title'    => 'Raport '.date("Y-m-d"), 
    'post_content'  => 'Add your content here',
    'post_type'     => 'raport',
    'post_status'   => 'draft',
    'post_author'   => $userid


This draft awaits for admins to take some action. It can be edited etc., and on edit page the permalink designed for this post is OK.

After admins perform certain actions, the system publishes the post automatically:


But after that the post's permalink is being broken. Instead of standard permalink (that wa visible on edit page):


the post's permalink after wp_publish_post looks like that:


which directs to custom post's type archive page, not to the post itself.

How can I fix it and publish these posts without breaking the permalinks?

2 Answers 2


Although the name of the function wp_publish_post() suggests that it can be used to publish a post it should obviously not be used to publish a draft post programmatically.

The way to do so is to use wp_update_post() and set the post status manually to publish:

// Update post with the ID 42
$postData = [ 'ID' => 42, 'post_status' => 'publish' ];
wp_update_post( $postData );

This will create a valid value for the post_name field and thus a proper permalink.

The function wp_publish_post() is used by WordPress only for publishing future posts during a scheduled hook (see check_and_publish_future_post()). The reason why this won't break permalinks is, that posts with status future already have a valid value for post_name created by wp_insert_post().

  • The docs for wp_publish_post() even mention that post_name will not be set. What they fail to mention is that this has some serious issues, like breaking the permalink for that post.
    – maryisdead
    Commented Oct 20, 2020 at 16:09

When drafts are published, the permalink is rewritten from the title of the post. Originally I thought maybe you could use the $original_slug parameter in the wp_unique_post_slug filter, but quickly found that to be unreliable in testing it.

If you have a method for persisting the draft slug in the global scope (such as through a class property or global) you should be able to overwrite the slug on publish via the wp_unique_post_slug hook with something like this:

 * Ensure the draft slug carries through when publishing a draft post.
 * @param string $slug          New post slug from title.
 * @param int    $post_ID       Post ID.
 * @param string $post_status   New post status.
 * @param string $post_type     Post type.
 * @param int    $post_parent   Post parent ID.
 * @param string $original_slug Original post slug.
 * @return string (maybe) filtered post slug.
function wpdocs_restore_slug_on_publish( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
    if ( $slug !== 'your-draft-slug' ) {
        $slug = 'your-draft-slug';
    return $slug;
add_filter( 'wp_unique_post_slug', 'wpdocs_restore_slug_on_publish', 10, 6 );

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.