--- project: cloudflare-cache-rules type: session-notes status: active tags: - pbs - cloudflare - caching - production - wordpress created: 2026-03-30 updated: 2026-03-30 path: PBS/Tech/Sessions/ --- # Cloudflare Cache Rules - Fix & Cleanup - March 30, 2026 ## Problem After production server migration (clone → update → promote), Cloudflare cache broke admin functionality. Jenny's post edits were reverting on save — the editor was serving stale cached versions of wp-admin pages. Both the IP-based bypass rule and the wp-admin path bypass rule were failing. ## Root Causes Found ### 1. Cloudflare Cache Rules use "last match wins" Unlike Page Rules (first match wins), Cache Rules are **stackable** — all matching rules apply, and for conflicting settings, the **last matching rule wins**. Our bypass rules were ordered above the Cache Everything rule, so Cache Everything was always winning. ### 2. Missing leading slashes on URI paths The wp-admin bypass rule had `wp-admin` instead of `/wp-admin`, so it never matched actual request paths like `/wp-admin/post.php`. ### 3. Invalid n8n/Gitea entries in path rules Full URLs (`https://n8n.plantbasedsoutherner.com`) were in URI Path filters, which only match path portions (like `/wp-admin/`). These entries were doing nothing. n8n and Gitea already have Cloudflare proxy disabled, so cache rules aren't needed for them. ### 4. Staging URL contamination 473 references to `staging.plantbasedsoutherner.com` were baked into the WordPress database from the server migration — post content, Elementor data, postmeta, GUIDs, and plugin caches. ## What We Fixed ### Cache Rule Reordering Moved Cache Everything to position **1** (top) with bypass rules below it. Since last match wins, the bypass rules now correctly override caching. **Current rule order:** 1. Cache Everything (2hr Edge TTL, 1 day Browser TTL, serve stale while revalidating) 2. Bypass Cache - PBS Admins (cookie: `pbs_admin_bypass`) 3. Bypass Cache - WP Admin Paths (`/wp-admin`, `/wp-login.php`) 4. Bypass Cache - WooCommerce (`/cart`, `/checkout`, `/my-account`, `woocommerce` cookie) ### URI Path Fixes - Added leading slashes: `/wp-admin`, `/wp-login.php`, `/cart`, `/checkout`, `/my-account` - Removed invalid full-URL entries for n8n and Gitea ### Cookie-Based Admin Bypass - Replaced IP-based bypass with custom `pbs_admin_bypass` cookie - Confirmed working via Cloudflare Trace and DevTools (`CF-Cache-Status: DYNAMIC`) - Cookie set manually in DevTools for now — WPCode snippet for auto-set on admin login is next ### Staging URL Cleanup - Ran WP-CLI search-replace inside Docker container: ``` docker exec -it wordpress wp search-replace 'staging.plantbasedsoutherner.com' 'plantbasedsoutherner.com' --all-tables --allow-root ``` - 473 replacements made across wp_posts, wp_postmeta, and GUIDs - Flushed Redis object cache: `wp cache flush --allow-root` - Manually cleared stale plugin caches: Jetpack site icon, Astra partials cache - Deleted `astra_partials_config_cache` from wp_options to force regeneration ### Debugging Tools Used - Cloudflare **Trace** (Rules → Trace): Simulates a request and shows which rules match in order — confirmed both bypass and Cache Everything were firing, with Cache Everything winning - Browser DevTools Network tab: Checked `CF-Cache-Status` response header - `curl` blocked by Cloudflare bot challenge (Bot Fight Mode) — use DevTools instead ## Key Learnings - **Cloudflare Cache Rules: last match wins** — opposite of Page Rules. Put broad rules first, specific overrides below. - **URI Path filters only match paths** — never full URLs with hostnames - **Always include leading slashes** in URI path filters (`/wp-admin` not `wp-admin`) - **Server cloning contaminates URLs** — always run `wp search-replace` after migrating/cloning WordPress between environments - **WP-CLI handles serialized data safely** — never do raw SQL find-and-replace on WordPress content (Elementor data can break) - **`wp-config.php` tip**: Add `define('FS_METHOD', 'direct');` to suppress FTP credentials prompt in Docker ## Still To Do - [ ] WPCode Lite snippet to auto-set `pbs_admin_bypass` cookie on admin login - [ ] Clean up Rule 2 extra expressions (leftover `tjherbranson` wildcard and `starts_with` conditions) - [ ] Create scoped Cloudflare API token (Zone.Cache Purge only) - [ ] Build n8n auto-purge workflow (WordPress publish/update → Cloudflare targeted cache purge) - [ ] Decide: auto-purge homepage on content updates?