Unified Caching for PHP & JavaScript: CDN, Browser, Server Tuned for Core Web Vi
Written by on Tuesday, September 30th, 2025
The Caching Strategy Guide for PHP and JavaScript Websites: Aligning CDN, Browser, and Server Layers With Core Web Vitals and SEO
Introduction
Caching is the quiet workhorse behind fast, resilient websites. For PHP and JavaScript stacks, the right blend of CDN, browser, and server-side caching can compress time-to-first-byte, deliver instant repeat views, and stabilize performance under traffic spikes—all while protecting SEO signals and Core Web Vitals. The wrong mix, however, can surface stale pages, personalization leaks, broken shopping carts, and confused crawlers. This guide provides a layered blueprint you can apply to a WordPress or Laravel build, a React or Next.js app, and everything in between. You will learn how to set cache headers, shape cache keys, tune TTLs, invalidate safely, and measure improvements in user experience and search visibility.
Why Caching Matters for Core Web Vitals and SEO
Core Web Vitals and search crawlers both reward fast, predictable delivery. Caching affects multiple user-centric metrics:
- LCP (Largest Contentful Paint): CDNs and preloaded assets shorten the path to the hero image or primary content block. Aggressive browser caching of CSS and fonts keeps render-blockers off the critical path on repeat visits.
- INP (Interaction to Next Paint): Reduced main-thread pressure from fewer network requests and smaller payloads leads to snappier interactivity. Server-side and edge caching reduces CPU churn for HTML generation, making initial interaction smoother.
- CLS (Cumulative Layout Shift): Cached fonts, precise image dimensions, and preloading help stabilize layout quickly, reducing unexpected shifts.
- TTFB (Time to First Byte): Server and CDN caching directly reduce TTFB by serving content closer to the user and skipping dynamic computation.
From an SEO perspective, fast TTFB and consistent delivery can improve crawl efficiency (crawl budget) and reduce the frequency and cost of render-blocking fetches. Proper use of validators (ETag and Last-Modified) enables Googlebot to refresh content without downloading full payloads, while safe TTLs on robots.txt and sitemaps ensure search engines see updates promptly.
The Three-Layer Model: Browser, CDN/Edge, Origin/Server
Think in layers, with each layer owning clear responsibilities and constraints:
- Browser cache: Holds static assets (JavaScript, CSS, fonts, images) and sometimes HTML via service workers. Ideal for long-lived, immutable content keyed by URL.
- CDN/Edge cache: Sits geographically close to users, caching both static and cacheable HTML or API responses. It terminates TLS and enforces caching policies with fast invalidation tools.
- Origin/Server cache: Includes PHP OPcache, object caching (Redis/Memcached), page caches, and SSR response caches. It’s the source of truth that feeds upper layers efficiently.
Aligning the three prevents conflicts like a long CDN TTL masking an urgent publish, or a service worker serving an old shell after a product recall. The ideal setup defines TTLs, validators, cache keys, and purge flows as a single system.
Browser Caching: Headers and Patterns That Deliver Speed
Browsers obey HTTP headers and make decisions per resource. Your job is to set clear policy signals:
- Use
Cache-Control: public, max-age=31536000, immutable
for versioned assets likeapp.9f3a2.js
,styles.1a2b.css
, andlogo.7c8d.webp
. Theimmutable
directive tells browsers not to revalidate during a session, eliminating conditional requests. - For unversioned or frequently updated files (e.g., HTML), prefer
Cache-Control: no-cache
with a strong validator (ETag
) orLast-Modified
so browsers can cheaply confirm freshness and receive304 Not Modified
. - Avoid mixing
no-store
unless the content is truly sensitive (e.g., personal dashboards).no-store
defeats beneficial browser caching and can hurt performance metrics. - Use
Vary
sparingly.Vary: Accept-Encoding
is standard;Vary: User-Agent
creates cache fragmentation and higher miss rates.
Implement asset versioning (a.k.a. cache busting) to unlock long TTLs. Bundle or chunk with content hashes in filenames. In PHP stacks, update enqueue/version parameters in WordPress or mix-manifest in Laravel. In JS builds, let bundlers emit hashed filenames and ensure references update in HTML templates or server-rendered markup.
Service Worker Strategies for JavaScript-Heavy Apps
Service workers add a programmable caching tier in the browser. Choose a strategy per route or asset class:
- Cache First for static shell assets (framework runtime, fonts, icons). Combine with a revision manifest to purge old entries.
- Stale While Revalidate for content API calls, delivering a fast response and refreshing in the background.
- Network First for highly dynamic or sensitive content (user-specific dashboards) to prevent stale views.
Keep service worker caches small and observable. Version the worker file itself so the browser picks up updates promptly, and test “skipWaiting” flows carefully to avoid disrupting in-flight sessions.
Preload, Preconnect, and Priority Hints
Resource hints can make caching even more effective by front-loading critical fetches:
<link rel="preload" as="style" href="/styles.1a2b.css">
for render-blocking CSS.<link rel="preload" as="image" imagesrcset="hero-1200.webp 1200w, hero-800.webp 800w" imagesizes="100vw" href="hero-1200.webp">
to accelerate the LCP image.<link rel="preconnect" href="https://cdn.example.com">
anddns-prefetch
for critical third-party origins.fetchpriority="high"
on your LCP image andlow
on below-the-fold assets to guide scheduling.
CDN/Edge Caching: The Performance Multiplier
A CDN turns regional latency into near-local speed. Tune edge caching to amplify origin capacity and stabilize vitals:
- Prefer
Cache-Control: public, s-maxage=86400, stale-while-revalidate=30, stale-if-error=86400
for HTML that can tolerate short staleness windows.s-maxage
targets shared caches (CDN), while browsers still respectmax-age
if set; omitmax-age
for browser validation while CDNs serve aggressively. - Define cache keys intentionally. The default key is usually scheme + host + path + query. Ignore tracking params by default (e.g.,
utm_*
,gclid
) to prevent fragmentation. Only include cookies or headers in the key when truly necessary. - Set “do not cache” rules for
Authorization
headers and session-bearing cookies. One stray cookie can cause a CDN miss for all visitors. - Use versioned URLs for assets and long TTLs (days to a year). For HTML, shorter TTLs plus background revalidation often strike the right balance.
Prefer tag-based invalidation over path-only purges. Systems like Fastly Surrogate-Keys or Cloudflare Cache Tags let you purge all product-detail pages when a SKU updates, without blowing away the entire cache. Automate purges with deployment hooks or CMS webhooks to ensure content changes propagate instantly.
Edge Logic Without Killing Cache Hit Rate
Personalization at the edge (workers/functions) can collapse cache efficiency if not designed carefully. Apply segmented strategies:
- Serve a cached base HTML with Edge Side Includes (ESI) or edge functions stitching in small personalized fragments, each with its own short TTL.
- Partition only when necessary (e.g., currency at country level) and keep the number of variants low.
- Use geolocation or A/B test IDs as explicit cache key components only when they affect the response, and provide short TTLs to avoid stale experiments.
For APIs, leverage Surrogate-Control
or s-maxage
to keep CDNs authoritative while browsers validate. Short edge TTLs with stale-while-revalidate
hide origin latency spikes from users and bots.
Server-Side Caching for PHP and JavaScript
At the origin, caching reduces CPU work and database chatter. Align your tiers:
- Bytecode: PHP OPcache is non-negotiable. Ensure adequate memory, interned strings enabled, and preloading for frameworks.
- Object cache: Store computed fragments and query results in Redis or Memcached. For WordPress, persistent object cache dramatically reduces DB load on traffic spikes.
- Page cache: Save rendered HTML to disk or memory for anonymous traffic. Gate by auth cookies and vary rules. Microcache bursts (1–5 seconds) can smooth thundering herds.
- SSR response caches: For Next.js/Nuxt or custom Node SSR, cache route-level HTML or JSON render results with revalidation policies (e.g., ISR with
revalidate
seconds).
Framework specifics:
- WordPress/WooCommerce: Use a page cache plugin or reverse proxy (Nginx, Varnish) for anonymous pages. Exclude cart, checkout, and account pages; strip cookies for general pages to maximize CDN hits.
- Drupal: Lean on cache tags and max-age metadata; pair with Fastly or Cloudflare for tag-aware purging.
- Laravel: Cache routes and config, use Redis for query and view caches, and set HTTP caching via middleware for API responses.
- Next.js: Use
Cache-Control
headers per route, ISR for semi-static pages, and edge cache for middleware-proxied assets. Avoid dynamic cookies on cacheable routes.
Aligning Policies Across Layers
Misalignment is the root of stale bugs and poor hit rates. Establish a contract:
- Immutable assets: Long browser
max-age
plus CDN TTL measured in months; purge not required because URLs change on deploy. - HTML and APIs: Short to medium
s-maxage
at the CDN, browsers set tono-cache
for cheap validation, and origin page/object caches tuned to expected traffic patterns. - Validators: If you use
ETag
, make it stable across origins behind a load balancer. Weak ETags (W/
) orLast-Modified
can be safer than per-server strong hashes. - Purges: CI/CD triggers tag or path purges at the CDN; CMS events do the same on publish/update/delete. Origin page caches receive targeted invalidation, not global flushes.
Document precedence. For instance, CDN rules override origin Cache-Control
only in specific cases, and service workers must always respect server-sent versioning. This avoids phantom stale states where the browser and CDN disagree about freshness.
SEO-Safe Caching Patterns
Search engines need fast access to fresh, canonical content. Use the following practices:
- 200 vs 304: Conditional GETs with
ETag
orIf-Modified-Since
let bots refresh content cheaply. Ensure HTML responses provide validators and allow revalidation. - Canonical and hreflang: Cache headers must not strip or vary away canonical tags. Don’t cache different canonicals behind geographic variants unless reflected in URLs.
- Robots and sitemaps: Keep
robots.txt
TTL short (e.g.,max-age=300
) to react to emergencies. Sitemaps can be cached longer (e.g., hours) but revalidate on content deploy. - Vary: Overusing
Vary
can fragment cache and confuse crawlers. UseVary: Accept-Language
only when content truly differs; otherwise rely on on-page language attributes or URL segmentation. - Cookies and query params: Strip non-functional parameters and ignore marketing cookies at the edge so Googlebot sees consistent URLs and receives cache hits.
Ensure pre-rendering or SSR is cache-aware. If bots receive client-side rendered placeholders due to cache misses or JS delays, LCP and indexing can suffer. Cache SSR HTML at the edge with a short s-maxage
, then refresh in the background with stale-while-revalidate
.
Core Web Vitals Playbook by Page Type
Homepage or Landing Pages
- Cache HTML at the CDN with
s-maxage
between 300 and 1800 seconds;stale-while-revalidate
30–60 seconds to mask re-render times. - Preload the hero image and critical CSS; cache images and CSS for one year with hashed filenames.
- Use low JS payloads and delay non-essential scripts. Large bundles undercut LCP even when cached.
Article or Documentation Pages
- Immutable URLs per versioned content help cache forever at the CDN, with purges on updates. For frequently edited content, use tag-based purges triggered by the CMS.
- Serve pre-sized images, modern formats (WebP/AVIF), and long TTLs.
- Ensure TOC and code highlighting JS is either deferred or split into small chunks with cache-busting.
Product Listing and Product Detail Pages
- PLPs: Cache HTML for a short window at the edge; use API calls for inventory facets with
s-maxage
andstale-if-error
to avoid blank states. - PDPs: Cache per SKU and purge by tag on price or stock changes. Personalize cart widgets via client-side or small ESI fragments with micro-TTLs.
Authenticated Dashboards
- Mark responses
private, no-store
for sensitive pages. Use object caches to speed backend queries. - Cache non-sensitive fragments (e.g., help tips, feature flags) aggressively and combine with client-side hydration.
Compression, Formats, and Transport
Caching lowers frequency of transfers; compression reduces their size. Serve Brotli (br
) over HTTPS and gzip as a fallback. Precompress static assets at build time and ensure the CDN selects the right encoding via Vary: Accept-Encoding
. For images, prefer WebP/AVIF and lazy-load below-the-fold assets. HTTP/2 and HTTP/3 reduce connection overhead; avoid over-bundling as multiplexing makes many small, cacheable chunks viable when properly prioritized and versioned.
Testing and Measurement: Close the Loop
Observe caching in both synthetic and field conditions:
- Chrome DevTools: Network panel shows from-disk/from-memory status, headers, and priority. Use the Application tab to inspect service worker caches.
- Lighthouse and PageSpeed Insights: Evaluate LCP, INP, CLS, and opportunities. Pair with the Chrome User Experience Report (CrUX) to see field results.
- WebPageTest: Test multiple locations, cold/warm cache, and repeat views to gauge browser caching benefits. Trace waterfall to confirm preloads and priorities.
- CDN analytics: Monitor hit ratio, origin offload, and edge latencies. Segment by path and content type to find cache fragmentation.
- Server metrics: Track TTFB, DB query counts, Redis hit rates, and queue times. Alert on cache stampedes and elevated 5xx rates.
Adopt canary deploys: roll out new caching rules to a small percentage at the edge, verify metrics and error rates, then scale up. Record changes and correlate with Core Web Vitals trends in RUM dashboards to validate impact.
Troubleshooting and Anti-Patterns
- Cache stampede: When popular content expires, many concurrent misses can hammer the origin. Use request coalescing at the CDN (origin shielding) and at the origin with locks. Prefer
stale-while-revalidate
to serve stale content while refreshing. - ETag mismatches behind a load balancer: Strong ETags based on per-server inode or timestamp cause endless 200s. Switch to weak ETags or Last-Modified and standardize generation across nodes.
- Cookie pollution: Marketing or experimentation cookies on every route can force
Cache-Control: private
interpretations at some CDNs. Strip unneeded cookies at the edge for cacheable pages. - Over-broad purges: Clearing entire caches on every deploy thrashes hit rates. Use content-derived tags and targeted invalidations.
- Service worker ghosts: Old workers can serve legacy shells. Version workers, manage
clients.claim()
andskipWaiting()
carefully, and provide a kill-switch path. - Double compression: Ensure only one layer compresses on-the-fly or serve precompressed assets. Review headers to avoid gzipping already gzipped files.
- Clock skew: If origin and CDN clocks differ greatly, max-age and cache validators behave unpredictably. Sync with NTP across infrastructure.
Implementation Blueprint: WordPress on Nginx with Cloudflare
- Origin: Enable PHP-FPM with OPcache; add Redis for persistent object cache; use a page cache plugin writing static HTML for anonymous users.
- Headers: Serve assets with
Cache-Control: public, max-age=31536000, immutable
and hashed filenames. HTML usesCache-Control: no-cache
withETag
orLast-Modified
. - CDN rules: Cache everything for HTML with
s-maxage=600, stale-while-revalidate=30
on anonymous paths. Bypass cache on cart/checkout/account and when WooCommerce session cookies are present. Striputm_*
params from cache keys. - Purge: On publish/update, trigger a cache tag purge for the post, archives, and homepage. Avoid full-zone purges.
- Images and fonts: Serve WebP/AVIF, preload LCP image on templates, and preconnect to the CDN host.
- Verification: Run WebPageTest for cold and repeat views; inspect Cloudflare analytics for hit ratio; watch Redis hits and origin CPU during spikes.
Implementation Blueprint: Laravel with Redis and CloudFront
- Origin caching: Enable route and view caches. Store expensive queries and fragment results in Redis with TTLs. Add response caching middleware for public endpoints.
- HTTP headers: For JSON APIs that are cache-safe, set
Cache-Control: public, s-maxage=60, stale-while-revalidate=30
and a stableETag
. Invalidate selectively on data changes. - CloudFront behavior: Separate cache behaviors per path (e.g.,
/assets/*
long TTL,/api/*
short TTL). Remove cookies and headers from the cache key unless required. - Invalidation: Use Lambda@Edge or build pipelines to issue targeted invalidations by path or manifest references on deploy.
- Security and SEO: Ensure
robots.txt
small TTL and immediate invalidation on policy changes; cache sitemaps with moderate TTL and purge on content publish.
Implementation Blueprint: React/Next.js with Edge Caching and a Service Worker
- Build: Emit hashed chunks and CSS. Mark fonts for long-term caching and add
preload
for the primary font subset. - ISR: Configure
revalidate
per page type (e.g., 5 minutes for blog, 30 seconds for product). Align CDNs-maxage
withrevalidate
to avoid serving expired HTML. - Service worker: Cache First for framework runtime and icons; Stale While Revalidate for content JSON endpoints; Network First for user-specific data.
- Edge middleware: Avoid adding cookies or headers that enter the cache key on public pages. If geolocation affects currency, partition by country and keep variants small.
- Testing: Confirm LCP improvements by comparing cold vs warm navigations and first vs repeat visits in CrUX or your RUM tool.
Advanced Topics: Microcaching, ESI, and Surrogate-Control
Microcaching (1–10 seconds) at Nginx or Varnish flattens brief surges without meaningfully increasing staleness. It pairs well with background refresh and request coalescing. Edge Side Includes let you assemble pages from separately cached fragments, useful for mixing long-lived navigation/footer with short-lived promo blocks. If your CDN supports Surrogate-Control
, use it to set edge-specific TTLs while keeping browser directives conservative, enabling aggressive edge caching without risking stale browser content.
Caching for APIs and Headless Architectures
Headless setups benefit from resource-based caching:
- Cache by resource ID and version. For example,
/api/posts/123?ver=9
enables long edge TTLs with purges on version bump. - Use
ETag
andLast-Modified
for conditional GETs, returning304
when unchanged. - Apply
Vary: Accept
only if you produce different representations (e.g., JSON vs HTML) from the same URL. Otherwise, prefer distinct endpoints. - Throttle and coalesce: At the edge, enable request collapsing so only one origin fetch happens during a miss spike.
Governance, Observability, and Maintenance
Cache systems require ongoing care to stay effective and safe:
- Ownership: Assign cache ownership to a platform team with clear escalation paths. Document TTLs, cache keys, and purge flows.
- Runbooks: Provide emergency steps to disable or bypass caches, purge critical content globally, and roll back service worker changes.
- Budget and SLAs: Track cache hit ratios, origin offload, and P95 TTFB. Set thresholds for when to tweak TTLs or add cache layers.
- Change management: Every deploy that alters URLs, headers, or manifests should include cache implications in the review checklist.
- Auditing: Quarterly audits should verify headers, test purges, and ensure robots/sitemaps caching remains appropriate as content cadence changes.
When aligned, browser, CDN, and server caches transform the performance profile of PHP and JavaScript applications. Properly configured, they lift Core Web Vitals, reduce infrastructure cost, and amplify SEO impact—all while keeping content accurate and secure through disciplined invalidation and observability.