Skip to content

Auto-Purge Cache for CPT Archive & Sidebar Pages on Publish

Created 2026-07-07
Status planned

When a new post is published (or an existing one edited) in any of the custom post types — communications, podcast, articles, testimonials, events — the listing/archive pages and any sidebar that displays that content should purge automatically at every cache layer. No more manual “purge this page” after each publish.

The capability already exists and is half-wired. It lives in one existing mu-plugin and only needs completing.

  • The auto-purge engine is cloudflare-auto-purge.php (v1.4.0), already on the server at sites/baseworks.com/mu-plugins/cloudflare-auto-purge.php.
  • It already hooks save_post (line 315) for all post types, skips autosaves/revisions, purges the post’s own URL, and — via bw_get_related_pages_for_post_type() (lines 93–119) — purges “related listing pages” for that post type.
  • That map currently covers only two post types: event and post. The four CPTs the user cares about (communications, podcast, article, testimonial) are simply not in the map. That is the entire reason their archives go stale.

Therefore:

  • Do not create a separate plugin. It would duplicate the Cloudflare auth helpers and URL-variation logic already in this file for zero benefit.
  • Do not touch bw-four-layer-cache-purge.php — that is only the admin-bar “4-Layer Cache Purge” button, not the auto-purge engine. The two files are easy to conflate but are separate concerns.
  • Do extend bw_get_related_pages_for_post_type() and add matching nginx-layer coverage in cloudflare-auto-purge.php.

The two-layer trap (why “just add rows to the map” is not enough)

Section titled “The two-layer trap (why “just add rows to the map” is not enough)”

The related-pages map is consumed only inside bw_purge_cloudflare_on_post_save() — i.e. it only purges Cloudflare.

  • nginx-helper’s own purge_archive_on_edit purges the CPT archive URL (/article/) but never the friendly Elementor listing page (/blog/, /communication/, etc.).
  • Events dodge this today with a site-wide nginx purge_all on save_post_event (lines 335–345), justified because events publish infrequently and nginx rebuilds fast.

So if we only add four rows to the Cloudflare map, Cloudflare goes fresh but nginx keeps serving the stale archive. Completing the feature means each listing/sidebar URL must be purged at both layers.

Good news: the bw_fix_nginx_helper_cache_key filter (added 2026-06-25, lines 366–401) makes per-URL nginx purge reliable regardless of xCloud’s trailing-slash cache-key state, by deleting both key variants. So we now have a surgical option instead of the blunt purge_all.

Open questions to resolve BEFORE writing code

Section titled “Open questions to resolve BEFORE writing code”

These must be answered first — the plan is gated on them.

1. Confirm the real friendly listing URLs (do NOT guess from CPT slugs)

Section titled “1. Confirm the real friendly listing URLs (do NOT guess from CPT slugs)”

The human-facing pages do not match the register_post_type rewrite slugs. Proven from docs/ELEMENTOR-CSS-CACHING-ISSUE.md and the CPT registrations:

Post typeCPT rewrite slug (archive)Real friendly listing pageSource
post(blog)/blog/ + /ja/blog/already in map ✅
eventevent/events/, /events-archive/ + /ja/already in map ✅
communicationscommunications (plural)/communication/ (singular)Elementor doc ✅
podcastpodcast/podcast/Elementor doc ✅
testimonialtestimonial (singular)/testimonials/ (plural)Elementor doc ✅
articlearticleUNKNOWN — confirm on live site⚠️
instructorinstructor/instructors/in scope? (see Q3)

Note the singular/plural flips for communications and testimonials — a map built from CPT slugs would purge the wrong URL. Before build:

  • Confirm /communication/, /podcast/, /testimonials/ are the live listing pages (and whether each also has a CPT-archive page worth purging).
  • Find the real listing page for articles (/article/? /articles/? folded into /blog/?).
  • Confirm the /ja/ counterpart exists for each.

2. What is “the sidebar,” and where does it render?

Section titled “2. What is “the sidebar,” and where does it render?”

The user mentioned “sidebar purges.” This is the biggest scope-determining question:

  • If the sidebar is part of each listing/archive page, purging those page URLs already covers it — no extra work.
  • If the sidebar is a site-wide widget (e.g. “latest posts / upcoming events” shown on many pages, or on every single-post view), then its purge surface is every page that renders it, which no listing-URL map covers. That would require either enumerating those pages or accepting a broader purge.

Action: identify which pages carry the sidebar and whether it pulls cross-type “latest” content. This answer decides whether this is a ~5-URL map extension or something wider.

The user did not name instructors (they rarely change). Recommend: leave out for now, add later if desired. Confirm.

Proposed implementation (once questions answered)

Section titled “Proposed implementation (once questions answered)”

All changes in sites/baseworks.com/mu-plugins/cloudflare-auto-purge.php.

Section titled “Step 1 — Extend the Cloudflare related-pages map”

Add confirmed rows to bw_get_related_pages_for_post_type():

$post_type_pages = array(
'event' => array(
$site_url . '/events/',
$site_url . '/ja/events/',
$site_url . '/events-archive/',
),
'post' => array(
$site_url . '/blog/',
$site_url . '/ja/blog/',
),
// NEW — URLs below pending confirmation (Q1):
'communications' => array(
$site_url . '/communication/',
$site_url . '/ja/communication/',
),
'podcast' => array(
$site_url . '/podcast/',
$site_url . '/ja/podcast/',
),
'testimonial' => array(
$site_url . '/testimonials/',
$site_url . '/ja/testimonials/',
),
'article' => array(
// $site_url . '/<confirmed-article-listing>/',
),
);

Homepage (/ and /ja/) is already always purged — good, since it often shows cross-type latest content.

Step 2 — Give the nginx layer the same coverage (the actual fix)

Section titled “Step 2 — Give the nginx layer the same coverage (the actual fix)”

Pick ONE approach and apply it to the new CPTs:

  • Option A — surgical (recommended). After the Cloudflare purge in bw_purge_cloudflare_on_post_save(), also run nginx-helper’s per-URL purge on each related listing URL. The bw_fix_nginx_helper_cache_key filter now makes this reliable. More targeted; no site-wide flush.
  • Option B — proven/blunt. Mirror the event pattern: register save_post_communications, save_post_podcast, etc. handlers that call nginx_helper()->purge_all(). Simple and battle-tested, but flushes the whole nginx cache on every publish of these types.

Recommendation: Option A, because the both-variant filter that made it reliable did not exist when the event purge_all workaround was written — the surgical path is now the better default. Keep Option B as the fallback if per-URL nginx purge proves flaky in testing for these specific URLs.

  • If sidebar = part of listing pages → already covered by Steps 1–2.
  • If sidebar = site-wide widget → add the sidebar-bearing page URLs to the purge set (or, if unbounded, fall back to the event-style purge_all for these publishes).

Testing (one type end-to-end before wiring the rest)

Section titled “Testing (one type end-to-end before wiring the rest)”

Per the repo’s server-touching guardrails, prove it on one post type first:

  1. Pick communications. Publish/update a test communication.
  2. Confirm in error_log the [cloudflare-purge] line lists /communication/ (+ /ja/) and the homepage.
  3. Hard-load /communication/ in an incognito window (bypassing browser cache) and confirm the new post appears without a manual purge.
  4. Verify both Cloudflare (cf-cache-status header flips to MISS then HIT) and nginx (new content served) went fresh.
  5. Only after this passes, add podcast → article → testimonial the same way, testing each.
  • This is a mu-plugin edit on production baseworks.com. Follow the state-what/why/done sequence and confirm before applying on the server.
  • Keep the save_post hook (fires on edits too — desirable; autosaves and revisions are already skipped). No need to switch to transition_post_status.
  • Do NOT trigger Elementor CSS flushes — targeted URL purges do not delete Elementor CSS files, so this stays clear of the Feb 4 broken-CSS incident.
  • Mirror the edited file back into the repo (sites/baseworks.com/mu-plugins/) and update docs/CODE-INVENTORY.md after deploy.
  • Bump the plugin version header and add a dated Updated: note.

Small. Once Q1–Q3 are answered: ~1 focused session — a few rows in one map, one nginx-purge helper loop, and per-type testing. No new plugin, no new infrastructure.