Cache Strategy & Cloudflare Configuration
Status: ✅ Deployed
Last Updated: 2026-01-23
Problem
After deployments, users would see stale content because:
- Service workers cached old bundles and kept serving them until browser cleared storage
- Cloudflare cached HTML shells for extended periods (1 hour)
- Browser caches combined with SW caches meant users needed incognito to see updates
Solution
1. Tighter Cache Headers (public/_headers)
HTML shells now have Cache-Control: no-cache, no-store, must-revalidate:
- Cloudflare revalidates the HTML on every request
- Browsers don't serve stale copies without checking
Critical non-hashed files also bypass cache:
/sw.jsand/service-worker.js→ always fetch fresh (SW updates picked up immediately)/manifest.webmanifest→ refreshes with new icons/config/version.json→ used for cache invalidation tracking
Hashed assets (/assets/*.*) stay cached aggressively (1 year) because file names change on content updates.
2. Cloudflare Cache Rules
Important: All custom domains (dev.ourlantern.app, docs.ourlantern.app, storybook, etc.) are Cloudflare Pages custom domains under the main ourlantern.app zone. Cache rules are applied once at the zone level and automatically apply to all subdomains.
Two methods to set up:
Method A: Manual (One-time setup)
# For dev environment
npm run cf:setup-cache-rules:dev
# For prod environment
npm run cf:setup-cache-rules:prodThis requires:
CLOUDFLARE_ZONE_IDenv var (or in.env.local)CLOUDFLARE_API_TOKENenv var (or in.env.local)CLOUDFLARE_ACCOUNT_IDenv var (or in.env.local)
The script will display the exact cache rules to create and a direct link to your Cloudflare Dashboard.
Also ensure Browser Cache TTL is set to "Respect Existing Headers" in Caching → Configuration. This lets our tight cache-control headers take effect on the browser side.
Rules created (free-tier compatible):
- Bypass cache for
/and/index.html - Bypass cache for
/sw.js,/service-worker.js - Bypass cache for
/manifest.webmanifest,/version.json - Cache 1 year for
/assets/(prefix match viastarts_with)
Method B: Cloudflare Dashboard
If API setup fails or you prefer manual config:
- Go to Cloudflare Dashboard → Caching → Cache Rules
- Create rules in this order:
| Rule | When | Then |
|---|---|---|
| HTML Shell (Root & Index) | (http.request.uri.path eq "/" or http.request.uri.path eq "/index.html") | Bypass Cache |
| Service Workers | (http.request.uri.path eq "/sw.js" or http.request.uri.path eq "/service-worker.js") | Bypass Cache |
| Manifest & Version | (http.request.uri.path eq "/manifest.webmanifest" or http.request.uri.path eq "/version.json") | Bypass Cache |
| Hashed Assets | starts_with(http.request.uri.path, "/assets/") | Cache (TTL: 1 year) |
3. Automated Cache Purge on Deploy
Both CI workflows now purge critical files after deployment:
Deployed URLs:
.github/workflows/deploy-dev.yml→ purgeshttps://dev.ourlantern.app/*.github/workflows/deploy-prod.yml→ purgeshttps://ourlantern.app/*
Files purged after every deploy:
/(root)/index.html/sw.js/service-worker.js/manifest.webmanifest/version.json
This ensures:
- Hashed assets remain cached (unchanged filename = no purge needed)
- HTML shells are always fresh
- Service workers check for updates on next visit
- New deployments are live within minutes
Browser-Side Clearing (For Users/Testing)
If a user still sees old content after deployment:
- Open DevTools → Application tab
- Service Workers → Click Unregister
- Clear Storage → Select all storage types → Clear Site Data
- Hard refresh (Cmd+Shift+R or Ctrl+Shift+R)
- Alternatively: Incognito window to test clean state
Verification Checklist
After setup, verify the configuration:
# Check _headers is correct
cat public/_headers | grep -A 5 "HTML shell"
# Verify deployment purges cache (after next deploy)
# Check CI logs for: "✅ Cache purge complete"
# Test cache headers (should show no-cache for HTML)
curl -I https://dev.ourlantern.app/index.html
# Look for: Cache-Control: no-cache, no-store, must-revalidate
curl -I https://dev.ourlantern.app/assets/main-*.js
# Look for: Cache-Control: public, max-age=31536000, immutableWhat This Fixes
✅ Users see updates without manual storage clearing
✅ Service worker updates deploy immediately
✅ Hashed assets stay cached (browser performance preserved)
✅ HTML shells always reflect latest deployment
✅ Cloudflare + browser caches work together (not against each other)
Files Modified
public/_headers— Cache control directives.github/workflows/deploy-dev.yml— Enhanced purge step.github/workflows/deploy-prod.yml— Enhanced purge stepscripts/setup-cloudflare-cache-rules.mjs— Cache rule setup toolpackage.json— Addedcf:setup-cache-rules:*scripts