Skip to main content

CDN and Cache Fundamentals

Learning Focus

By the end of this lesson you will understand what Cloudflare caches by default, how to control caching with headers and rules, and how to purge cached content when needed.

What Is a CDN?

A Content Delivery Network (CDN) is a distributed network of servers that stores copies of your content at locations closest to your users. Instead of every request traveling to your origin server (which might be in one data center), requests are served from the nearest edge node (Point of Presence / PoP).

Before CDN vs After CDN

Without CDNWith Cloudflare CDN
LatencyEvery request goes to origin (200–500ms)Served from nearest PoP (~20ms)
Origin loadAll traffic hits your serverOnly cache misses reach origin
Bandwidth costsYou pay for all egressCloudflare absorbs cached traffic
AvailabilitySingle point of failureDistributed across 330+ locations
CostEgress fees from cloud providersFree (Cloudflare doesn't charge for bandwidth)

What Cloudflare Caches by Default

Cloudflare caches static files based on file extension. If a request URL ends with one of these extensions, Cloudflare will cache it automatically:

CategoryExtensions
Images.jpg, .jpeg, .png, .gif, .webp, .svg, .ico, .bmp, .tif, .tiff
Scripts/Styles.js, .css
Fonts.woff, .woff2, .ttf, .eot, .otf
Documents.pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx
Media.mp3, .mp4, .ogg, .wav, .avi, .mov, .webm
Web.html, .htm (only with specific cache headers)
Other.swf, .zip, .gz, .tar
Important

By default, Cloudflare does not cache HTML pages. HTML is considered dynamic content and is always fetched from the origin. You can override this behavior with Cache Rules.

Cache Status Headers

Every response from Cloudflare includes a cf-cache-status header that tells you whether the content was served from cache:

Check cache status
curl -sI https://example.com/style.css | grep cf-cache-status

# cf-cache-status: HIT
StatusMeaning
HITContent served from Cloudflare's cache (fastest)
MISSContent not in cache — fetched from origin, now cached for next request
EXPIREDContent was in cache but expired — re-validated with origin
BYPASSCloudflare was instructed to skip cache (e.g., by a cache rule or no-cache header)
DYNAMICCloudflare treats this as uncacheable content (default for HTML)
REVALIDATEDCache was stale, origin confirmed content hasn't changed (304 response)

Cache-Control Headers

You control caching behavior from your origin server using the Cache-Control HTTP header:

Cache-Control: public, max-age=3600, s-maxage=86400
DirectiveMeaning
publicAny cache (including CDN) can store this response
privateOnly the browser can cache this (CDN must not cache)
max-age=NBrowser caches for N seconds
s-maxage=NCDN/shared cache caches for N seconds (overrides max-age for CDN)
no-cacheCache must revalidate with origin before serving
no-storeDo not cache at all — anywhere
must-revalidateOnce expired, must check origin before serving stale content
Content TypeHeaderReasoning
Static assets (JS, CSS, images)public, max-age=31536000, immutableVersioned files (with hash in filename) — cache forever
HTML pagespublic, max-age=0, must-revalidateAlways check for fresh content
API responsesprivate, no-cacheUser-specific data, no CDN caching
Fontspublic, max-age=31536000Fonts rarely change
tip

Use content hashing in your asset filenames (e.g., app.a1b2c3.js) and set max-age=31536000 (1 year). When you update the file, the hash changes, so the browser fetches the new version. This gives you both maximum caching and instant updates.

Cache Rules

Cache Rules let you customize caching behavior beyond the defaults — directly in the Cloudflare dashboard, without modifying your origin server.

Common Cache Rules

Cache Everything (Including HTML)

By default, Cloudflare doesn't cache HTML. To cache entire pages:

SettingValue
WhenURI Path contains /blog/
ThenCache eligibility: Eligible for cache
Edge TTL1 hour
Browser TTL5 minutes

Bypass Cache for Admin Pages

SettingValue
WhenURI Path starts with /admin or /wp-admin
ThenCache eligibility: Bypass cache

Long Cache for Static Assets

SettingValue
WhenURI Path starts with /assets/ or /static/
ThenEdge TTL: 1 month
Browser TTL1 year

Free Plan Limits

FeatureFree Plan
Cache Rules10 rules
Edge TTL control
Browser TTL control
Bypass cache
Cache Everything

Purging Cache

When you update content on your origin, the CDN might still serve the old cached version. Purging clears the cache so Cloudflare fetches fresh content.

Purge Methods

MethodUse CaseHow
Purge EverythingMajor site updateDashboard → Caching → Purge Everything
Purge by URLUpdated a specific page or assetDashboard → Caching → Custom Purge → Enter URL(s)
Purge by TagPurge all assets matching a cache tag (Enterprise)API with Cache-Tag header
Purge by PrefixPurge all URLs under a path (Enterprise)API with URL prefix

Purge via API

Purge specific URLs
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{
"files": [
"https://example.com/style.css",
"https://example.com/app.js"
]
}'
Purge everything
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{"purge_everything": true}'
caution

Purge Everything clears all cached content across all edge nodes. The next request for every resource will be a cache miss, which can spike traffic to your origin. Use targeted purges whenever possible.

Edge TTL vs Browser TTL

Understanding the difference between these two TTLs is critical:

Edge TTLBrowser TTL
WhereCloudflare's edge serversVisitor's browser
ControlsHow long Cloudflare caches the contentHow long the browser caches the content
Purge possible?✅ Yes (via dashboard or API)❌ No — you can't purge a visitor's browser cache
HeaderControlled by s-maxage or Cache RulesControlled by max-age

Common Misconceptions

"Cloudflare caches everything including HTML by default"

Reality: By default, Cloudflare only caches static assets (images, JS, CSS, fonts). HTML is treated as "DYNAMIC" and always fetched from origin. Use Cache Rules to cache HTML.

"Purging cache is instant worldwide"

Reality: Purging propagates across Cloudflare's 330+ PoPs within ~30 seconds in most cases, but it's not truly instantaneous.

"More caching is always better"

Reality: Caching user-specific content (dashboards, authenticated pages) creates security risks where one user could see another user's data. Only cache truly public, static content.

Anti-Patterns to Avoid

Don't Do ThisDo This Instead
Cache authenticated/personalized pagesUse private, no-cache for user-specific content
Set very short Edge TTLs (< 1 minute)Use reasonable TTLs (1 hour+) and purge when content changes
Purge Everything after every deployPurge specific URLs or use cache-busting filenames
Ignore cf-cache-status headerMonitor cache HIT ratios to optimize performance
Never set Cache-Control headers on originExplicitly set headers to control behavior

Key Takeaways

  • Cloudflare CDN caches static assets by default — images, JS, CSS, fonts — for free.
  • HTML is not cached by default. Use Cache Rules to cache static HTML pages.
  • The cf-cache-status header tells you whether content was served from cache.
  • Use Cache-Control headers on your origin to control caching behavior.
  • Cache Rules (10 free) let you customize caching without changing your origin server.
  • Purge responsibly — prefer targeted URL purges over purging everything.

What's Next

  • Continue to Speed Brain to learn about Cloudflare's prefetch acceleration.