CDNs don’t just store copies of your content closer to users; they actively decide how long to keep those copies around and when to get rid of them, often in ways that surprise people.
Let’s look at a typical asset request flow through a CDN. Imagine a user in Tokyo requests https://example.com/images/logo.png.
- User Request: The browser in Tokyo hits
https://example.com/images/logo.png. - DNS Resolution: DNS directs the request to the nearest Akamai/Cloudflare/Fastly edge server (let’s say, in Tokyo).
- Edge Server Check: The Tokyo edge server checks its local cache for
logo.png.- Cache Hit: If it’s there and not expired, it serves the image directly to the user. Fastest path.
- Cache Miss: If it’s not there, or expired, the edge server forwards the request to the origin server (e.g.,
origin.example.com).
- Origin Server Response: The origin server sends
logo.pngback to the Tokyo edge server, along with HTTP headers likeCache-ControlandExpires. - Edge Server Caching: The edge server stores
logo.pngin its cache according to the caching directives in the origin headers. It also sets its own internal TTL (Time To Live) based on these headers. - Serve to User: The edge server then serves
logo.pngto the Tokyo user.
The magic (and complexity) lies in steps 3, 5, and the directives from step 4.
Edge Rules: The First Line of Defense
Edge rules, often configured in your CDN provider’s dashboard, let you override or supplement origin caching behavior. They’re powerful because they can make decisions before the request even hits your origin.
Common Use Cases & Configuration:
- Overriding TTLs: Your origin might set a short TTL for an asset, but you want the CDN to hold it longer.
- Diagnosis: Check your CDN’s "Edge Rules" or "Rules Engine" configuration. Look for rules matching the URL path (
/images/*or specific file patterns). - Fix Example (Cloudflare):
- Rule:
If URL matches *.png - Action:
Cache Level: Cache Everything - Action:
Edge Cache TTL: 1 year
- Rule:
- Why it works: This tells the CDN edge server to cache the asset aggressively for a full year, regardless of what the origin might suggest for shorter periods.
- Diagnosis: Check your CDN’s "Edge Rules" or "Rules Engine" configuration. Look for rules matching the URL path (
- Ignoring Origin Cache Headers: Sometimes you want the CDN to cache everything from a specific path, even if origin headers say
Cache-Control: no-cache.- Fix Example (Akamai Property Manager):
- Rule:
Match: URL path contains /assets/ - Action:
Cache Settings: Cache by URL - Action:
Default TTL: 30 days
- Rule:
- Why it works: This forces the CDN to cache all responses from
/assets/for 30 days, ignoring any potentially conflictingCache-Controldirectives from the origin for those specific requests.
- Fix Example (Akamai Property Manager):
- Setting Cache Keys: You can control what makes a cached object unique. By default, it’s usually the URL. You might want to vary it by query string or even a custom header.
- Fix Example (Fastly VCL - simplified):
sub vcl_recv { if (req.url ~ "^/api/") { set req.cache_key = req.url; # Default # Add query string to cache key if present if (req.url ~ "\?") { set req.cache_key = regsub(req.url, "\?.*", "?"); set req.cache_key = req.cache_key + querystring; } } } - Why it works: This ensures that
/api/users?id=123and/api/users?id=456are treated as distinct cache entries, while/api/usersmight use a different rule.
- Fix Example (Fastly VCL - simplified):
TTLs: How Long Does It Stay?
TTL (Time To Live) is the duration an object is considered fresh in the cache. It’s primarily controlled by HTTP headers sent from your origin server, but can be influenced by edge rules.
Cache-ControlHeader: The modern standard.max-age=<seconds>: The most common directive.Cache-Control: public, max-age=31536000means cache for one year.s-maxage=<seconds>: Likemax-age, but specifically for shared caches (like CDNs).no-cache: Doesn’t mean "don’t cache." It means "cache, but revalidate with the origin before serving."no-store: Truly means "do not cache."
ExpiresHeader: The older HTTP/1.0 way.Expires: Wed, 21 Oct 2025 07:28:00 GMT.Cache-ControloverridesExpiresif both are present.
Diagnosis: Use your browser’s developer tools (Network tab) to inspect the Cache-Control and Expires headers for the specific asset. Or, use curl -I <URL> from a machine not on your local network to simulate an external request.
Fix Example (Nginx Configuration): To set a long TTL for static assets:
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ {
expires 365d; # Sets Cache-Control: max-age=31536000
add_header Cache-Control "public";
# Other directives like gzip, etc.
}
Why it works: This Nginx configuration adds Cache-Control: public, max-age=31536000 to responses for matching file types, telling the CDN (and browsers) to cache them for one year.
Common Pitfall: Setting max-age=0 or no-cache on frequently changing dynamic content. This forces a round trip to the origin for every request, defeating the purpose of the CDN.
Invalidation: The "Oops, I Changed It" Button
What happens when you update a file but its TTL hasn’t expired? The CDN is still serving the old version. Invalidation is how you tell the CDN to remove an object from its cache before its TTL is up.
- URL-based Invalidation: The most common. You specify the exact URL(s) to purge.
- Diagnosis: If you deploy a new version of
style.cssand users are still seeing the old styles (becausestyle.csshas a long TTL), you need to invalidate. - Fix Example (Cloudflare API):
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/<ZONE_ID>/purge_cache" \ -H "X-Auth-Email: user@example.com" \ -H "X-Auth-Key: <API_KEY>" \ -H "Content-Type: application/json" \ --data '{"files": ["https://example.com/css/style.css"]}' - Why it works: This sends a specific instruction to Cloudflare’s edge servers to remove
https://example.com/css/style.cssfrom their caches immediately. The next request for it will be a cache miss.
- Diagnosis: If you deploy a new version of
- Tag-based Invalidation: More advanced. You can associate "tags" with assets. Invalidating a tag purges all assets with that tag. Useful for invalidating multiple related assets (e.g., all assets for a specific product page).
- Fix Example (Fastly API):
curl -X POST \ -H "Fastly-Key: <API_KEY>" \ -H "Content-Type: application/json" \ --data '{"key": "product_123_images"}' \ "https://api.fastly.com/service/<SERVICE_ID>/purge/key" - Why it works: If your origin server added
X-Cache-Tags: product_123_imageswhen serving images for product 123, this command purges all cached items associated with that tag.
- Fix Example (Fastly API):
- Wildcard Invalidation: Less common due to performance implications and potential for accidental purges. Purges all files matching a pattern.
- Fix Example (Akamai API - simplified):
# API calls vary widely by provider and version # This is a conceptual representation akamai-cli purge purge_by_uri --url "https://example.com/images/*" - Why it works: Tells Akamai edge servers to remove all cached objects whose URLs start with
https://example.com/images/.
- Fix Example (Akamai API - simplified):
The "Next Problem": After you’ve mastered caching TTLs and invalidation, you’ll start thinking about how to handle cache busting for versioned assets (e.g., app.v12345.js) and how these strategies interact with SSR/ISR rendering patterns.