Steven's Knowledge

Getting Started

Put Cloudflare in front of a site, set basic cache rules, and verify edge caching works

Getting Started

The fastest way to feel a CDN's value: front a real site with Cloudflare, configure caching, and verify with curl. Cloudflare's free tier covers the whole exercise; the patterns transfer to any other CDN.

Sign Up & Add a Site

  1. Sign up at cloudflare.com (free tier is enough).
  2. Add a Site → enter your domain.
  3. Cloudflare scans existing DNS records and shows them. Confirm.
  4. Change nameservers at your registrar to the two Cloudflare-provided ones. DNS propagation takes minutes to hours.
  5. Once active, Cloudflare proxies traffic for orange-cloud (🟠) DNS records.

For a quick test without changing real DNS, point a subdomain (cdn-test.example.com) at Cloudflare and play there.

Default Behavior

Out of the box, Cloudflare:

  • Caches static file extensions (CSS, JS, images, fonts, PDFs).
  • Doesn't cache HTML by default — most CMS responses look dynamic.
  • Terminates TLS at the edge with a free cert.
  • Compresses with Brotli / gzip when supported.
  • Enables HTTP/2 and HTTP/3.

You see the result in the cf-cache-status header:

curl -I https://example.com/style.css

# HTTP/2 200
# cf-cache-status: HIT          ← served from cache
# age: 3421
# cache-control: public, max-age=14400
StatusMeaning
HITServed from the edge cache
MISSNot in cache; fetched from origin and now cached
EXPIREDWas cached but TTL elapsed; re-fetched
REVALIDATEDTTL elapsed but origin returned 304
BYPASSA rule said don't cache
DYNAMICNot eligible (cookies, method, content type)
STALECache TTL expired, served stale (origin down or stale-while-revalidate)

Tell the CDN What to Cache

Origins control caching via response headers:

Cache-Control: public, max-age=31536000, immutable
Cache-Control: public, max-age=300, s-maxage=3600, stale-while-revalidate=60
Cache-Control: private, no-store

Key directives:

DirectiveMeaning
publicOK to cache anywhere (including CDN)
privateOnly the user's browser may cache
no-storeDon't cache anywhere
max-age=NBrowser cache TTL in seconds
s-maxage=NCDN/shared cache TTL (overrides max-age for CDN)
immutableBrowser won't revalidate within max-age
stale-while-revalidate=NAfter TTL, serve stale for N seconds while refreshing in background
stale-if-error=NServe stale for N seconds if origin errors
# Versioned static assets (hash in filename): cache forever
Cache-Control: public, max-age=31536000, immutable

# Public HTML page: short edge TTL with refresh
Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=60

# Per-user response: never share
Cache-Control: private, no-store

# API response that's the same for all users for 1 min
Cache-Control: public, s-maxage=60

Override at the CDN

Cloudflare lets you override caching with Page Rules / Cache Rules even if origin headers say otherwise:

URL pattern: example.com/blog/*
Cache Level: Cache Everything
Edge Cache TTL: 1 hour
Browser Cache TTL: 5 minutes

This is essential when the origin sends conservative headers but you want aggressive edge caching.

Purge When Content Changes

# Purge one URL
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://example.com/blog/my-post"]}'

# Purge by tag (requires setting Cache-Tag header on origin responses)
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
  -H "Authorization: Bearer $TOKEN" \
  --data '{"tags":["post-42"]}'

# Purge everything (nuclear option)
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
  -H "Authorization: Bearer $TOKEN" \
  --data '{"purge_everything":true}'

Wire purges into your deploy / CMS:

  • CMS publish → purge the page URLs.
  • Image processing finished → purge /images/{id}/*.
  • Deploy ships new bundle → purge /assets/* (or trust the hash-in-filename strategy and don't purge).

Verify End-to-End

# First request → MISS
curl -I https://example.com/blog/hello

# Repeated request → HIT
curl -I https://example.com/blog/hello

# After purge → MISS again
curl -X POST .../purge_cache --data '{"files":[...]}'
curl -I https://example.com/blog/hello

If you see cf-cache-status: DYNAMIC when you expected HIT, the most common causes:

  • Cookies in the request (Cloudflare bypasses cache when Cookie is set unless you tell it not to).
  • set-cookie in the response (same).
  • Vary: * or unhelpful Vary values.
  • Cache-Control: private or no-store on the response.
  • POST/PUT method (only GET/HEAD cached).

Inspect with curl -v to see exactly what's coming back.

Image Optimization

Most CDNs offer on-the-fly image transformations:

# Cloudflare Images / Polish
https://example.com/cdn-cgi/image/width=400,format=auto,quality=85/photo.jpg

# Fastly Image Optimizer
https://example.com/photo.jpg?width=400&auto=webp

# imgix (dedicated)
https://example.imgix.net/photo.jpg?w=400&auto=format,compress

The same source file gets resized, recompressed, and converted to webp/avif per request, then cached at the edge. Don't ship one giant JPG to every device; let the CDN serve the right size.

What's Next

You can put a CDN in front of a site and verify caching. Next:

On this page