Action Hook since 1.5.0

save_post

Fires after a post is saved or updated

Description

save_post runs after WordPress writes the post row. Use it for caches, sync, and derived data — and always guard against autosave, revisions, and infinite loops.

When it runs

After wp_insert_post() commits. Fires for autosaves and revisions unless you skip them.

Signature

do_action( 'save_post', int $post_id, WP_Post $post, bool $update );

Parameters

  • $post_id int — Saved post ID.
  • $post WP_Post — Post object after save.
  • $update bool — True when updating an existing post.

Examples

Basic

add_action( 'save_post', function( $post_id, $post ) {
    if ( $post->post_type !== 'book' ) {
        return;
    }
    delete_transient( 'book_list_' . $post_id );
}, 10, 2 );

Flush a per-post transient when a book is saved.

Real case

add_action( 'save_post', function( $post_id, $post, $update ) {
    if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
        return;
    }
    if ( $update ) {
        do_action( 'my_plugin_post_updated', $post_id );
    }
}, 10, 3 );

Skip autosaves/revisions and fire a custom event only on real updates.

Common Use Cases

  • Clear caches after post update
  • Send admin email notifications
  • Sync data to external APIs
  • Auto-generate related records
  • Queue heavy sync/recalculation work instead of running it inline on large tables

Common mistakes

  • Calling wp_update_post() inside save_post without removing the callback first — infinite loop
  • Trusting save_post as proof of a legitimate, authorized edit — it also fires from the REST API, importers, and programmatic wp_insert_post() calls, so sensitive actions still need their own current_user_can()/nonce checks.

Related hooks

FAQ

Why does save_post fire twice?

Once for the autosave/revision and once for the real post. Guard with wp_is_post_autosave() and wp_is_post_revision().

Is it safe to run heavy logic directly inside save_post on a large site?

Not if it's synchronous — save_post runs inside the save request itself, so slow logic on a large database can lock tables and delay the editor's save response. Offload it to a queue or scheduled event (e.g. Action Scheduler or wp_schedule_single_event) instead.

Source: wp-includes/post.php

Group wizard

One session: this hook, then each related_hooks entry. Walk it and tell us what to change.

save_post → init

functions.php — save_post
functions.php
PHP save_post · Hook 1 of 2 · Step 1 of 7

Practice the save_post WordPress action hook in our interactive sandbox — a VS Code-style editor with a step-by-step save_post tutorial, add_action exercises, and instant feedback. No local WordPress install required.