Cache Strategy โ
Purpose: Explain Lantern's multi-layered cache strategy for optimal performance and instant updates.
Overview โ
Lantern uses a three-tier cache strategy to balance performance with fresh content delivery:
- Cache Busting - Immutable assets with unique filenames
- Short TTL for HTML - 5-minute cache window for quick updates
- Emergency Purge - Manual purge script for critical situations
Layer 1: Cache Busting (Primary Strategy) โ
Implementation โ
Build-time filename generation with unique build IDs:
javascript
// vite.config.mjs
build: {
rollupOptions: {
output: {
entryFileNames: `assets/[name]-${buildId}.[hash].js`,
chunkFileNames: `assets/[name]-${buildId}.[hash].js`,
assetFileNames: `assets/[name]-${buildId}.[hash][extname]`,
},
},
}Build ID generation (scripts/generate-version.mjs):
- Creates
public/version.jsonwith unique build ID - Build ID format:
{timestamp}-{commit-sha-8} - Never cached by Cloudflare (
Cache-Control: no-cache)
Result โ
- JS/CSS assets: Cached forever (
max-age=31536000, immutable) - New deployment: New filenames = automatic cache bypass
- Zero stale assets: Changed files always have new URLs
Example:
Old: assets/main-20260120-abc12345.js
New: assets/main-20260122-def67890.jsLayer 2: Short TTL for HTML โ
Configuration โ
/*.html
Cache-Control: public, max-age=300, stale-while-revalidate=60, must-revalidate
/
Cache-Control: public, max-age=300, stale-while-revalidate=60, must-revalidate
/index.html
Cache-Control: public, max-age=300, stale-while-revalidate=60, must-revalidateBehavior โ
- max-age=300: HTML cached for 5 minutes at CDN edge
- stale-while-revalidate=60: Serve stale content while fetching fresh (better UX)
- must-revalidate: Force revalidation after expiry
Why 5 Minutes? โ
- Performance: Reduces origin hits for high-traffic periods
- Freshness: Users get updates within 5 minutes
- Balance: Good middle ground between instant updates (0s) and long caching (hours)
Layer 3: Purge on Deploy (Automatic) โ
Implementation โ
GitHub Actions (.github/workflows/deploy-dev.yml, .github/workflows/deploy-prod.yml):
yaml
- name: Purge Cloudflare Cache
run: |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.CLOUDFLARE_ZONE_ID }}/purge_cache" \
-H "Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN }}" \
-H "Content-Type: application/json" \
--data '{"files":["https://ourlantern.app/","https://ourlantern.app/index.html"]}'What Gets Purged โ
Only HTML files:
/(root)/index.html
Not purged:
- JS/CSS assets (cache busting handles these)
- Images/fonts (static, rarely change)
- Service worker (already has
no-cacheheaders)
Why Selective Purging? โ
- Efficiency: Only purge what needs updating
- Cost: Cloudflare has purge rate limits
- Performance: Keep static assets cached globally
Layer 4: Emergency Full Purge (Manual) โ
When to Use โ
Use full purge ONLY when:
- Cache busting isn't working (deployment issue)
- Wrong content served globally
- Critical bug requiring immediate flush
- Something is "on fire"
DO NOT use for:
- Normal deployments (automatic purge handles this)
- Minor updates (5-minute TTL handles this)
- Testing (use
?cacheBust=123query param)
Usage โ
bash
# Purge dev environment
./scripts/purge-cache.sh dev
# Purge production
./scripts/purge-cache.sh prod
# Nuclear option - purge everything
./scripts/purge-cache.sh allPrerequisites โ
Add to .env.local:
bash
CLOUDFLARE_ZONE_ID=your_zone_id_here
CLOUDFLARE_API_TOKEN=your_api_token_hereGet credentials:
- Zone ID: Cloudflare Dashboard โ Your Domain โ Overview (right sidebar)
- API Token: Cloudflare Dashboard โ My Profile โ API Tokens
- Required permission:
Zone.Cache Purge
- Required permission:
What Gets Purged โ
Everything:
- All HTML files
- All JS/CSS assets
- All images, fonts, icons
- All cached API responses
- Everything Cloudflare has cached
Impact โ
- Users: Slower load times until cache rebuilds
- Origin: Higher server load temporarily
- Cloudflare: May trigger rate limits if done repeatedly
Cache Flow Diagram โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. User visits ourlantern.app โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 2. Cloudflare CDN checks cache for index.html โ
โ โข Fresh (< 5 min): Serve from cache โ
โ โข Stale (> 5 min): Fetch new, serve old while wait โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3. HTML references assets with build IDs โ
โ <script src="/assets/main-20260122-abc123.js"> โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 4. CDN checks cache for JS/CSS โ
โ โข Hit: Serve from cache (1 year max-age) โ
โ โข Miss: Fetch from origin, cache for 1 year โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 5. App loads and checks version.json โ
โ โข Same version: Continue normally โ
โ โข New version: Auto-reload app โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโDeployment Flow โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. Developer pushes to main โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 2. CI/CD builds app โ
โ โข Generates new build ID โ
โ โข Creates version.json โ
โ โข Builds assets with new filenames โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3. Deploys to Cloudflare Pages โ
โ โข dist/ uploaded to CDN โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 4. Purges HTML from cache โ
โ โข / and /index.html only โ
โ โข Forces fresh HTML on next visit โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 5. Users get new version โ
โ โข Next visit: Fresh HTML (purged) โ
โ โข HTML loads new JS/CSS (new filenames) โ
โ โข Old assets ignored (no references) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโCache Headers Reference โ
Static Assets (JS/CSS/Images) โ
/assets/*
Cache-Control: public, max-age=31536000, immutable- public: Can be cached by CDN
- max-age=31536000: Cache for 1 year
- immutable: Browser won't revalidate (trusts it never changes)
HTML Files โ
/*.html
Cache-Control: public, max-age=300, stale-while-revalidate=60, must-revalidate- public: Can be cached by CDN
- max-age=300: Cache for 5 minutes
- stale-while-revalidate=60: Serve stale + fetch fresh in background
- must-revalidate: Force check after expiry
Service Worker โ
/sw.js
Cache-Control: no-cache, no-store, must-revalidate- no-cache: Always revalidate with origin
- no-store: Never cache
- must-revalidate: Force revalidation
Version File โ
/version.json
Cache-Control: no-cache, no-store, must-revalidateSame as service worker - always fetch fresh for version checks.
Troubleshooting โ
Users seeing old version after deployment โ
Likely causes:
- 5-minute HTML cache hasn't expired yet
- Browser cache (rare with proper headers)
- Service worker cache (should auto-update)
Solutions:
- Wait 5 minutes for automatic expiry
- Run
./scripts/purge-cache.sh prodfor immediate update - User hard refresh:
Ctrl+Shift+R/Cmd+Shift+R
Assets returning 404 after deployment โ
Likely causes:
- Build failed to generate assets
- Deployment uploaded incomplete dist/
- Old HTML referencing new assets (timing issue)
Solutions:
- Check GitHub Actions logs for build errors
- Verify dist/ contains all assets before deploy
- Run
./scripts/purge-cache.sh prodto clear stale HTML
High origin bandwidth usage โ
Likely causes:
- Cache headers misconfigured
- Excessive cache purging
- CDN not caching properly
Solutions:
- Verify headers:
curl -I https://ourlantern.app - Check Cloudflare Analytics โ Caching
- Reduce purge frequency if needed
Best Practices โ
DO โ โ
- Use cache busting for all versioned assets
- Keep HTML TTL short (5-10 minutes)
- Purge selectively (HTML only) on deploy
- Monitor cache hit rates in Cloudflare
- Test deployments in dev environment first
DON'T โ โ
- Don't cache HTML for hours (stale references)
- Don't purge everything on every deploy (wasteful)
- Don't rely solely on query params for cache busting
- Don't cache service workers
- Don't skip version.json generation
Related Documentation โ
- PWA Architecture - Service worker caching strategy
- Deployment Guide - Full deployment process
- CI/CD Guide - Automated deployment pipeline
- Cloudflare Setup - Domain and access configuration
Metrics to Monitor โ
Cloudflare Analytics:
- Cache hit rate (target: >95% for assets)
- Bandwidth savings (target: >80% from cache)
- Origin requests (should be low)
Performance:
- Time to First Byte (TTFB)
- Largest Contentful Paint (LCP)
- Cache invalidation latency
Version checks:
- Auto-reload frequency
- Service worker update success rate