Skip to main content

Caching Foundation

WordPress caching eliminates redundant PHP execution and database queries by storing rendered output at multiple layers — from the server edge to the user's browser. Most sites see their biggest TTFB and LCP gains by getting page cache and CDN cache correct before chasing deeper PHP or database optimizations.

Core Idea

A proper cache strategy involves layering Edge (CDN), Page (Server), and Object (Database) caches to serve content as quickly as possible while ensuring dynamic actions like checkout and admin functions remain uncached.

Cache Stack at a Glance

LayerPurposeHitsMisses
Browser cacheReuse assets already on the user's device.Repeat visits; static files.First visit; HTML.
CDN edge cache (APO/Tiered)Serve HTML/assets from a nearby POP.Public pages; static assets.First-ever region hits; bypassed cookies.
Server page cache (LSCache)Serve HTML from the web server without PHP.Public pages; guest users.Personalized, cart/checkout, logged-in pages.
Object cache (Redis)Speed DB-dependent queries when HTML isn't cached.Logged-in/admin/checkout; miss after purge.None (if disabled).

Action Explanation & Estimated Impact

Typical Ranges

Ranges are from real-world WordPress sites on decent VPS + Cloudflare. Your mileage varies by theme/plugins, geography, and traffic.

ActionWhy it MattersTypical Impact
Enable Server Page Cache (LSCache)Serves prebuilt HTML directly from server store; skips PHP/DB.TTFB 0.2–0.6s, LCP 0.4–1.0s, PSI +5–15
Enable Cloudflare APO + Tiered CacheHTML and assets at the edge; fewer origin trips; Tiered improves hit-rate globally.TTFB 0.3–0.9s global, LCP 0.3–0.8s, PSI +8–20
Set Browser Cache for static assets (1y immutable)Repeat visits reuse cached JS/CSS/fonts/images instantly.Repeat-visit LCP 0.3–0.7s, PSI +5–10
Turn on Redis Object Cache (persistent)Speeds DB reads/writes for uncached pages (logged-in, cart, search).Uncached TTFB 0.15–0.4s, INP steadier, PSI +3–8 on dynamic pages
Brotli @ CDN + HTTP/2/3Smaller transfers and better multiplexing; quicker first paint.FCP/LCP 0.1–0.3s
Guest Mode/Guest Optimization (LSCache)Ensures first-time visitors get optimized, cacheable HTML even before session starts.TTFB 0.2–0.4s, LCP 0.2–0.5s, PSI +4–8
ESI blocks for cart/minicartCache whole page; hole-punch tiny dynamic fragments.LCP 0.2–0.5s on Woo product/listing
Smart Purge + WarmupKeeps hit-rate high post-publish; avoids cold cache.Sustained TTFB/LCP stability, PSI +3–6 consistency
Stale-While-Revalidate @ CDNServes slightly stale HTML instantly while refreshing in background.Perceived TTFB near-zero on edge hits

Static vs Dynamic Strategy

Aggressive ON, long TTL for Server Page Cache. ON (APO), long TTL for CDN HTML Cache.

  • Highest scores; minimal exceptions.
  • Object cache is optional.
  • ESI is not needed.

Safe Baseline (LSCache + Cloudflare) with Explanations

AreaSettingWhy
LSCache CacheCache = ON, Cache Mobile = ON (separate)Delivers HTML from server store; separate mobile to avoid DOM/CSS drift.
LSCache TTLHTML 17 days, Front page 1 dayLong TTL maximizes hit-rate; purge keeps content fresh.
LSCache Do Not Cache/cart/, /checkout/, /my-account/, ?add-to-cart=Prevents caching personalized/transactional routes.
LSCache ESIESI = ON, ESI for cart/minicartHole-punch tiny dynamic blocks; keep page cacheable.
LSCache PurgeAuto Purge on post/page update; selective purgesKeeps only affected pages fresh; avoids full cold cache.
Cloudflare APOON + Tiered Cache + Brotli + Early HintsEdge HTML+assets, better global hit-rates, smaller transfers, faster start.
Cloudflare Cache RulesBypass for /wp-admin*, cookies like wordpress_logged_in_Prevent serving cached HTML to logged-in/admin.
Browser Cache (headers)Static assets: cache-control: max-age=31536000, immutableEnsures instant loads on repeat visits.

Expected impact (combined baseline): TTFB 0.5–1.3s, LCP 0.6–1.6s, PageSpeed +15–35 (desktop often >95 for static sites).

Verifying with CLI (commands + expected output)

info

Run from your workstation or server shell.

Check CDN/Server cache headers

check-headers.sh
curl -I https://example.com/

Expected output (key lines):

curl-output-headers
HTTP/2 200
x-litespeed-cache: hit
cf-cache-status: HIT
cache-control: max-age=0, must-revalidate, no-cache, no-store

Interpretation: HTML served from LSCache (hit) and Cloudflare (HIT). HTML often uses short/zero max-age; static assets differ.

Check a static asset

check-static-asset.sh
curl -I https://example.com/wp-content/uploads/2025/hero.webp

Expected output:

curl-output-asset
HTTP/2 200
cache-control: max-age=31536000, immutable
cf-cache-status: HIT
content-encoding: br

Interpretation: 1-year browser cache, immutable, edge hit, Brotli on.

Confirm no-cache route

check-bypass-route.sh
curl -I https://example.com/checkout/

Expected output:

curl-output-bypass
HTTP/2 200
x-litespeed-cache: miss, no-cache
cf-cache-status: BYPASS

Interpretation: Checkout bypasses caches as intended.

Purge & Warmup Patterns (with benefits)

PatternHowBenefitImpact
Selective PurgePurge updated post + homepage, categories, tagsKeeps TTL long elsewhereMaintains high hit-rate
Scheduled WarmupUse crawler to prefetch top pages after purgeFewer cold misses after deployTTFB/LCP steady after changes
Stale-While-RevalidateCDN serves stale briefly while refreshingZero-downtime feel on updatesTTFB feels ~0s on edge hits

Common Pitfalls & Fixes

SymptomLikely CauseFix
Users see old contentOver-long TTL + no purgeEnable auto-purge on update; selective manual purge.
Wrong version to logged-in usersEdge caching HTML without cookie bypassAdd cookie-aware bypass rules (WordPress auth cookies).
Cart/checkout brokenCached dynamic routesExclude /cart/, /checkout/, ESI cart.
Cache fragmentation (low hit-rate)Vary by user-agent/device/language too broadlyMinimize varies; only separate where layout truly differs.
Double optimization conflictsLSCache + another plugin both minifying/cachingLet LSCache handle page caching; JS/CSS handled by your chosen layer (e.g., Perfmatters).

Quick Lab (60-90 minutes)

Lab Instructions
  1. Baseline: Record TTFB/LCP/PSI before changes (mobile + desktop).
  2. Enable LSCache with rules above; purge all once.
  3. Enable Cloudflare APO + Tiered + Brotli; create bypass rule for admin/auth cookies.
  4. Set Browser Cache for assets (1y immutable).
  5. Turn on Redis (Object Cache Pro/Redis plugin) and confirm Object Cache: Enabled in WP.
  6. Warm critical routes (home, blog, category, product, product list).
  7. Re-test: first view and repeat view from two locations.
  8. Compare deltas: target TTFB 0.8s, LCP 1.0s (static), and PSI +20.

Checklist (print-ready)

View Checklist
  • LSCache page cache ON; mobile cache ON (separate).
  • Exclusions: /cart/, /checkout/, /my-account/, ?add-to-cart=.
  • ESI cart/minicart ON.
  • TTLs: HTML 17d; front page 1d.
  • Cloudflare APO + Tiered + Brotli + Early Hints ON.
  • Cache Rules: bypass admin + auth cookies.
  • Browser cache for static assets: max-age=31536000, immutable.
  • Redis object cache enabled (persistent).
  • Purge on update (selective), crawler warmup configured.
  • Verified with curl -I and PageSpeed/Lighthouse from two regions.

Cheat Sheet (one page)

View Cheat Sheet
TaskSetting
Server page cacheLSCache ON, TTL 17d, exclude cart/checkout/account
Edge HTMLCloudflare APO ON, Tiered ON, Brotli ON
BrowserStatic assets max-age=31536000, immutable
Object cacheRedis ON (persistent), monitor hit-ratio
Dynamic fragmentsESI for cart/minicart
PurgeAuto on update; selective manual purges
WarmupCrawler on sitemap/top pages after purge
Verifycurl -I headers, Lighthouse, WebPageTest
ConflictsOne tool for page cache; one for JS/CSS

What's Next