If you're an experienced developer, you may wish to skip to the code.

If you’ve ever done any work with building a plugin or building a feature of a theme that includes a custom meta box or that includes functionality that is fired on the save_post action, then you’ve likely seen WordPress save_post called twice.

The thing is, this is expected behavior, but because of how it’s used, you may not always want your code to execute until the user actually clicks on the save button.

So here’s a quick tip on how to properly manage the case when save_post is called twice.

Understanding Why It Happens

Before you actually begin to programmatically handle when save_post is called, it’s worth understanding what’s going on behind the scenes.

Generally speaking, here are some general tips about save_post:

  • Check out the docblock for the function. In the WordPress source, you’ll notice: “@uses do_action() Calls ‘save_post’ and ‘wp_insert_post’ on post id and post data just before returning.”
  • Posts go through an autosave process whenever it’s created and whenever it’s being drafted. As such, save_post is actually fired multiple times while a user is drafting a post.
  • When the user clicks “Save” or “Publish,” the function fires thus kicking off yet-another-round of the function being called.

Finally, it’s worth noting the the edit_post function fires once, but only when the user has actually edited an existing post.

As such, we’re left with having to resolve the problem of the save functionality being called twice, and not doing so until the user has clicked ‘Save’ or ‘Publish.’

Combat Saving Posts Twice

The resolution is simple: we need to make sure that we check the post_type at the beginning of each time the function is fired. To be complete, we can also check to see if the post is being automatically saved.

To do this, we can leverage two functions that are available the WordPress API:

  1. wp_is_post_revision
  2. wp_is_post_autosave

Both functions accept the current post ID as an argument which allows us to determine if it’s the time to save our custom data or not by making sure that it’s not a post revision and that it’s not an autosave:

if( ! ( wp_is_post_revision( $post_id) || wp_is_post_autosave( $post_id ) ) ) {
   // Save your work
} // end if

Lastly, this should all be done at the beginning of the function.

If I’ve forgotten anything or there’s a better way to manage this, please feel free to share in the comments.