Skip to content

Early-bird lifetime licence: $199 once, for the first 200 buyers only. See the offer

save_post

Fires after a post of any type is created or updated. Use it to save extra data or clear caches when content changes.

  • Type Action
  • From WordPress
  • Since 1.5.0
  • Fired in wp-includes/post.php

When it fires

WordPress fires save_post at the end of wp_insert_post(), once the post has been written to the database. That covers publishing and updating in the editor, Quick Edit, imports and the REST API, but also revisions, autosaves and moving a post to the bin.

If the edit screen has classic meta boxes, the block editor saves them in a second request, which fires save_post again.

Parameters

Parameters
$post_idint Post ID.
$postWP_Post Post object.
$updatebool Whether this is an existing post being updated.

Example: Record who last edited a post

PHP
add_action( 'save_post', function ( $post_id, $post, $update ) {
	if ( ! $update || wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
		return;
	}
	if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
		return;
	}
	if ( 'post' !== $post->post_type || ! current_user_can( 'edit_post', $post_id ) ) {
		return;
	}
	update_post_meta( $post_id, '_my_last_editor', get_current_user_id() );
}, 10, 3 );

Good to know

  • Use save_post_{post_type}, for example save_post_page, to run for one post type only.
  • Calling wp_update_post() inside this hook fires save_post again. Remove your callback first, or you'll create an endless loop.
  • Terms and custom fields sent through the REST API can be saved after this hook runs. If you need them, use wp_after_insert_post (WordPress 5.6 and later).

Move your snippets over this afternoon.

Importing changes nothing until you switch over, and your old shortcodes keep working.