How to invalidate cache when saving a post with hooks in PHP in WordPress

Contents

Overview

This article explains, in exhaustive detail, how to invalidate cache when saving a post in WordPress using hooks in PHP. It covers the WordPress lifecycle hooks you should use, how to avoid edge cases (autosaves, revisions, permission checks), strategies for different cache layers (object cache, page cache, CDNs, reverse proxies), examples for common caches (Varnish, Cloudflare), and patterns for safe and performant invalidation (asynchronous requests, queueing, surrogate keys). Each code example is provided as a ready-to-drop-in snippet. Follow the sections that match your stack.

Why cache invalidation on post save matters

  • Freshness: When you update a post, you usually want the public-facing content to update immediately.
  • Multi-layer caches: Modern WordPress sites may have multiple caches: transients or object cache, full-page cache plugins (WP Super Cache, W3 Total Cache), reverse proxies (Varnish), and edge CDNs (Cloudflare). Each layer may need explicit invalidation.
  • Granularity: Purging an entire site is easy but expensive. Targeted invalidation (single URL, surrogate-key, tag-based purge) is preferred.

Which WordPress hooks to use

You can react to several hooks choose one depending on needs:

  • save_post — Fires after a post is saved (created or updated). Common choice because it gives post ID and post object.
  • wp_insert_post — Fires after a post is inserted/updated. Similar to save_post.
  • transition_post_status — Useful if you need to detect status changes (draft → publish, publish → draft) and act differently.
  • deleted_post, trash_post, edit_post — For removals and other operations.

Basic pattern

Use save_post (or transition_post_status) and early-return for autosaves, revisions or if the post type isnt relevant. Then call your cache invalidation routine, preferably in a non-blocking/asynchronous or batched way to avoid slowing the post save.

Important safety checks inside the hook

  1. Reject autosave revisions (wp_is_post_autosave, wp_is_post_revision).
  2. Check post type if you only want to purge for posts, pages, or custom post types.
  3. If you only want to purge on publish events, inspect post status and previous status using transition_post_status or by fetching post data.
  4. Avoid infinite loops: do not call functions that trigger save_post again unless you guard with a static flag.
  5. Respect capability checks if needed for security-sensitive actions.

Cache layers and corresponding invalidation methods

  • Object cache / Transients: delete_transient(), wp_cache_delete(), wp_cache_flush()
  • Plugin page caches: Plugin APIs differ — use plugin-specific functions or clear file-based caches as documented by the plugin.
  • Reverse proxies (Varnish): PURGE or BAN requests to the proxy or use surrogate keys.
  • CDNs (Cloudflare, Fastly, etc.): Use the providers API to purge single files or tags.
  • Surrogate keys and tags: Best practice: attach tags/keys to cached responses so you can purge related resources efficiently.

Examples

Below are practical, complete examples for common scenarios. Each code block uses the required

 wrapper for PHP examples. Adjust to your environment and credentials.

1) Simple: Delete related object cache and transients on save

This snippet deletes a transient for a post and clears a named cache key in the object cache. Use when you cache fragments or computed values per-post.

post_type ) {
        return
    }

    // Delete a transient keyed to the post
    delete_transient( my_post_summary_ . post_ID )

    // Delete a custom object cache key (if using object cache)
    cache_key = my_post_meta_cache_ . post_ID
    wp_cache_delete( cache_key, my_group )

    // Optionally flush group of caches or other keys your app uses
    // wp_cache_flush() // not recommended on high-traffic sites
}
?>

2) Purge / BAN a Varnish cache for a specific URL

Varnish often supports PURGE or BAN methods. Many hosting environments map PURGE to a special endpoint or allow PURGE HTTP method. This example demonstrates sending a PURGE request using WordPress HTTP API. If your proxy doesnt accept PURGE, adapt to your environment or use a server-side socket.

 PURGE,
        timeout => 2,
        headers => array(
            Host => parse_url( permalink, PHP_URL_HOST ),
        ),
    )

    // Non-blocking: fire and forget using low timeout
    wp_remote_request( permalink, args )
}
?>

Notes and caveats for Varnish

  • Many setups require PURGE requests to come from trusted IPs running PURGE from PHP may fail if Varnish is on another host or not configured to accept the request.
  • Alternate approach: add a BAN endpoint on your application that Varnish trusts, and call it when a post saves. Or configure surrogate keys/tags.

3) Purging Cloudflare cache for the updated post URL via API

Cloudflare offers an API to purge cached files by URL. This method requires a Cloudflare token and zone ID. This example uses wp_remote_post to purge the single URL. For production, keep tokens in secure storage (wp-config.php or environment variables).

post_status ) {
        return
    }

    url = get_permalink( post_id )
    if ( ! url ) {
        return
    }

    // Put these in wp-config.php or environment variables
    cloudflare_zone_id = defined( CF_ZONE_ID ) ? CF_ZONE_ID : 
    cloudflare_token   = defined( CF_API_TOKEN ) ? CF_API_TOKEN : 

    if ( empty( cloudflare_zone_id )  empty( cloudflare_token ) ) {
        return // Nothing we can do
    }

    endpoint = https://api.cloudflare.com/client/v4/zones/ . cloudflare_zone_id . /purge_cache
    body     = wp_json_encode( array( files => array( url ) ) )

    response = wp_remote_post(
        endpoint,
        array(
            headers => array(
                Authorization  => Bearer  . cloudflare_token,
                Content-Type   => application/json,
            ),
            body    => body,
            timeout => 5,
        )
    )

    // Optionally inspect response for errors and log if needed
}
?>

4) Using surrogate keys / tags (best practice)

If you control how pages are cached at edge (e.g., Fastly, Varnish, or your CDN), use surrogate keys or cache tags. When serving a page, add a header like X-Cache-Tags: post-123,author-5,category-9. Then your purge API calls can target tags — you can purge all resources with tag post-123 when the post changes without touching other content.

This requires two pieces:

  • When rendering HTML responses, set a header with the resource tags.
  • When updating content, call the CDN/proxy API to purge by tag.

Example: Add header with surrogate keys in template (PHP)

ID
        tags[] = author- . post->post_author
        foreach ( wp_get_post_categories( post->ID ) as cat_id ) {
            tags[] = cat- . cat_id
        }
    }
    header( X-Cache-Tags:  . implode(  , tags ) )
}
?>

Example: Purge surrogate key via API (Fastly-like)

 key ) )
    wp_remote_post( url, array(
        headers => array(
            Accept => application/json,
            Content-Type => application/json,
            Fastly-Key => api_key,
        ),
        body => body,
        timeout => 5,
    ) )
}
?>

5) Queue invalidations to run asynchronously

Blocking network calls on save can slow the admin experience and sometimes fail silently. Better approach: record invalidation tasks (as postmeta, transient or custom table) and process them via WP-Cron or a background job (WP Background Processing, Action Scheduler). This allows retries and batching.

 url ) {
        // Call your purge function (Cloudflare or Varnish, etc.)
        my_purge_url_async( url )
    }

    delete_transient( cache_purge_queue )
}

function my_purge_url_async( url ) {
    // Perform non-blocking call with low timeout or use wp_remote_post with blocking false
    wp_remote_post(
        https://example.com/purge-endpoint, // adapt to your proxy/purge endpoint
        array(
            blocking => false,
            body     => array( url => url ),
            timeout  => 1,
        )
    )
}
?>

Plugin-specific cache hooks (examples)

If you use caching plugins, prefer their documented APIs:

  • WP Super Cache: use its provided functions or drop the cache directory. Some sites call the plugins clear functions. Example function names change between versions.
  • W3 Total Cache: provides w3tc_flush_all() or similar. See plugin docs.

Always consult plugin documentation before calling internal functions — they arent part of WordPress core and can change.

Testing and validation

  1. Update a post and then curl the public URL to confirm the updated HTML is served.
  2. Check response headers for cache indicators: server, x-cache, x-cache-tags, age, cf-cache-status, etc.
  3. Use browser devtools to confirm resources reflect the new content.
  4. For CDN APIs, verify API response codes and log errors.
  5. Test on staging before production, especially if your purge endpoints are privileged or can purge whole zones.

Performance and operational considerations

  • Dont flush entire caches on every save unless you truly need to. Use targeted invalidation.
  • Rate-limits: CDNs and proxies often have rate limits. Batch purges where possible.
  • Security: Store keys and tokens securely do not hard-code them into themes. Use environment variables or wp-config constants.
  • Retries: If purge requests can fail, queue them and retry with exponential backoff rather than blocking the save action.
  • Logging: Log purge failures to error log or monitoring tool to catch configuration issues.

Edge cases and troubleshooting

  • Autosaves and revisions cause duplicate calls — always filter them out.
  • If your purge requests succeed but stale content persists, check whether the cache key you are purging matches the one used at cache time (surrogate keys must match exactly).
  • When using host-provided Varnish/CDN, sometimes there is an extra layer (edge origin) — ensure youre purging the correct layer.
  • Some hosts strip custom headers verify headers are actually present in the served response.

Practical checklist before deploying

  1. Identify each cache layer in your architecture.
  2. Decide granularity of invalidation (URL, tag, surrogate key).
  3. Implement safe checks in save_post handlers.
  4. Make requests non-blocking or queued to avoid slow admin saves.
  5. Securely store API keys, and keep permissions minimal.
  6. Test on staging for correctness and performance.

Quick reference code snippets

1) Minimal save_post pattern (no external calls):


2) Delete a transient for homepage or archives when a post is updated:


Useful links

Summary

Invalidating cache on post save is a crucial part of keeping content fresh. Use the appropriate WordPress hooks, guard against autosaves and revisions, and target the cache layer with the least impact (prefer tag/surrogate-key-based purges). Make purge requests asynchronous or queued to preserve admin performance, and secure your purge endpoints and API credentials. Applying these patterns will keep your site responsive and consistent across caches.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

Your email address will not be published. Required fields are marked *