Cloudflare CDN Hands-On: Cache Rules, Instant Purge, and URL Rewrites for Faster Web Apps
Cloudflare can make a “normal” web app feel snappy by caching the right things at the edge (assets, images, sometimes even HTML) while keeping truly dynamic routes uncached. The trick is being intentional: pick what to cache, how long, and how to invalidate it safely.
In this hands-on guide, you’ll set up a practical caching strategy using Cache Rules, automate cache invalidation with the Purge Cache API, and clean up legacy URLs using URL Rewrite (Transform Rules). All examples are designed for junior/mid devs working on typical web apps (React/Next.js, Laravel, Django, etc.).
What we’ll build
- Cache static assets aggressively (immutable files like
/assets/app.9c1f2.js) - Cache images and fonts for a long time
- Bypass cache for sensitive/dynamic routes (
/api,/admin, authenticated pages) - Optionally cache HTML for marketing pages with a short TTL
- Purge by URL from a deploy script when content changes
- Rewrite old URLs to new paths without changing what users see
Step 0: Prerequisites (quick checklist)
- Your DNS record is proxied through Cloudflare (orange cloud). Cache Rules require proxied traffic. :contentReference[oaicite:0]{index=0}
- You can access the Cloudflare dashboard for your zone.
- You have (or can create) an API token with permissions like
Zone.Cache Purge(for purging) and rule-edit permissions if you automate rules.
Step 1: Add Cache Rules (the practical “safe defaults”)
Cache Rules are Cloudflare’s modern way to control caching behavior per-request (think “if request matches X, apply caching settings Y”). :contentReference[oaicite:1]{index=1}
Below are three rules that work well for most web apps. Create them in the dashboard (Cache Rules page) or via API. :contentReference[oaicite:2]{index=2}
Rule A: Cache static assets for a long time
Goal: cache fingerprinted files (hash in filename) very aggressively. These rarely change; when they do, the filename changes too.
- Match:
URI Pathstarts with/assets/OR ends with.css,.js,.map - Action:
Cache Everything - Edge TTL: e.g.
30 days(or longer)
Why it’s safe: if your build outputs hashed filenames (recommended), users won’t see stale assets because new deployments produce new URLs.
Rule B: Cache images & fonts long-term
- Match: file extensions like
.png,.jpg,.webp,.svg,.woff2 - Action:
Cache Everything - Edge TTL: e.g.
30 days
If you’re serving user-uploaded images that can be replaced at the same URL, keep the TTL shorter (or plan to purge those URLs after updates).
Rule C: Bypass cache for sensitive/dynamic routes
Goal: never cache endpoints that are user-specific or frequently changing.
- Match:
URI Pathstarts with/api/,/admin/,/login,/account - Action:
Bypass cache
This is the “oops-prevention” rule. Cloudflare explicitly supports bypassing cache per rule. :contentReference[oaicite:3]{index=3}
Optional Rule D: Cache marketing HTML briefly
If you have mostly-static pages like /, /pricing, /docs, you can cache the HTML at the edge for a short time (like 5–10 minutes) to reduce origin load.
- Match: exact paths
/,/pricing,/about - Action:
Cache Everything - Edge TTL:
5 minutes
Don’t do this for authenticated pages or anything personalized. If your HTML varies by cookie/header, you’ll need a more advanced approach (or keep HTML uncached).
Step 2: Automate rule creation with the Cloudflare API (optional, but useful)
If you want to version-control your caching logic, Cloudflare documents creating Cache Rules via API by updating a zone ruleset. :contentReference[oaicite:4]{index=4}
Example curl pattern (you’ll need your ZONE_ID and RULESET_ID):
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/$RULESET_ID" \ --request PUT \ --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ --header "Content-Type: application/json" \ --data '{ "rules": [ { "enabled": true, "description": "Cache static assets", "action": "set_cache_settings", "expression": "(http.request.uri.path starts_with \"/assets/\") or (http.request.uri.path matches \"\\\\.(css|js|map)$\")", "action_parameters": { "cache": true, "edge_ttl": { "mode": "override_origin", "default": 2592000 } } }, { "enabled": true, "description": "Bypass API", "action": "set_cache_settings", "expression": "(http.request.uri.path starts_with \"/api/\") or (http.request.uri.path starts_with \"/admin/\")", "action_parameters": { "cache": false } } ] }'
Important: Cloudflare’s example warns that some “set all rules” requests can overwrite existing rules if used directly, so treat API updates carefully (fetch current rules first, then merge). :contentReference[oaicite:5]{index=5}
Step 3: Purge cache safely (purge by URL, not “purge everything”)
Cloudflare recommends purging specific URLs so you invalidate only what changed. Their docs call single-file purge (purge by URL) the recommended method. :contentReference[oaicite:6]{index=6}
Here’s a working example to purge a list of URLs:
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \ --request POST \ --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ --header "Content-Type: application/json" \ --data '{ "files": [ "https://example.com/pricing", "https://example.com/assets/app.9c1f2.js", "https://example.com/images/hero.webp" ] }'
If you’re using custom cache keys that vary by headers (device type, country, language), Cloudflare notes you may need to include those headers in the purge request to target the correct variants. :contentReference[oaicite:7]{index=7}
A deploy-friendly Node.js purge script
This is handy when you deploy a marketing site, update docs, or regenerate an RSS feed and want changes visible immediately.
/** * purge-cloudflare.js * Usage: * CLOUDFLARE_API_TOKEN=... ZONE_ID=... node purge-cloudflare.js \ * https://example.com/pricing https://example.com/sitemap.xml */ const [,, ...urls] = process.argv; if (!process.env.CLOUDFLARE_API_TOKEN || !process.env.ZONE_ID) { console.error("Missing CLOUDFLARE_API_TOKEN or ZONE_ID env vars"); process.exit(1); } if (!urls.length) { console.error("Provide one or more URLs to purge"); process.exit(1); } async function main() { const res = await fetch(`https://api.cloudflare.com/client/v4/zones/${process.env.ZONE_ID}/purge_cache`, { method: "POST", headers: { "Authorization": `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ files: urls }), }); const data = await res.json(); if (!res.ok || data.success !== true) { console.error("Purge failed:", JSON.stringify(data, null, 2)); process.exit(1); } console.log("Purged:", urls.length, "URL(s)"); } main().catch((err) => { console.error(err); process.exit(1); });
Tip: keep a small list of “always purge on deploy” URLs (like /, /pricing, /sitemap.xml, /rss.xml) rather than purging your entire cache.
Step 4: Fix legacy URLs with URL Rewrite (Transform Rules)
When you move routes (e.g., /blog/... becomes /articles/...), you can either redirect users (URL changes in browser) or rewrite internally (serve new content while keeping the old URL visible). Cloudflare’s URL Rewrite Rules do the latter. :contentReference[oaicite:8]{index=8}
Two practical uses:
- Keep old links working without changing bookmarks
- Normalize messy URLs (encoded slashes, legacy patterns)
Example rewrite concept:
- If request path starts with
/blog/ - Rewrite to
/marketing/(or whatever your new section is)
Cloudflare’s examples gallery includes patterns like rewriting everything under /blog/<PATH> to /marketing/<PATH>. :contentReference[oaicite:9]{index=9}
Gotcha: URL Rewrite rules can rewrite path/query, but community guidance notes you can’t rewrite the hostname with Transform Rules; for host/origin changes you’d use other features (like Origin Rules). :contentReference[oaicite:10]{index=10}
Debugging checklist: “Why isn’t it caching?”
- Rule order matters: ensure your bypass rules for
/apidon’t accidentally match broad paths you wanted cached. - Check headers: Cloudflare adds cache-related headers (like
CF-Cache-Status) that help you see HIT/MISS at a glance. - Don’t cache personalized HTML: if you see “wrong user data” even once, stop caching that route and revisit your strategy.
- Purge the exact URL: if your site uses both
httpandhttpsor multiple hostnames, purge the one Cloudflare cached.
A simple, safe Cloudflare caching strategy (recap)
- Cache immutable assets long (best ROI, lowest risk)
- Cache media long (or purge on updates)
- Bypass
/api,/admin, auth, and user-specific pages - Optionally cache HTML briefly for public marketing pages
- Purge by URL as part of deploy/content updates
- Rewrite legacy paths to keep old links alive without messy app-side routing hacks
If you apply just Rules A–C and add a small purge script, you’ll usually see faster repeat loads, reduced origin load, and fewer “why is it stale?” surprises—without turning your CDN into a debugging nightmare.
::contentReference[oaicite:11]{index=11}
Leave a Reply