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.

  1. User Request: The browser in Tokyo hits https://example.com/images/logo.png.
  2. DNS Resolution: DNS directs the request to the nearest Akamai/Cloudflare/Fastly edge server (let’s say, in Tokyo).
  3. 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).
  4. Origin Server Response: The origin server sends logo.png back to the Tokyo edge server, along with HTTP headers like Cache-Control and Expires.
  5. Edge Server Caching: The edge server stores logo.png in 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.
  6. Serve to User: The edge server then serves logo.png to 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
    • 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.
  • 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
    • Why it works: This forces the CDN to cache all responses from /assets/ for 30 days, ignoring any potentially conflicting Cache-Control directives from the origin for those specific requests.
  • 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=123 and /api/users?id=456 are treated as distinct cache entries, while /api/users might use a different rule.

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-Control Header: The modern standard.
    • max-age=<seconds>: The most common directive. Cache-Control: public, max-age=31536000 means cache for one year.
    • s-maxage=<seconds>: Like max-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."
  • Expires Header: The older HTTP/1.0 way. Expires: Wed, 21 Oct 2025 07:28:00 GMT. Cache-Control overrides Expires if 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.css and users are still seeing the old styles (because style.css has 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.css from their caches immediately. The next request for it will be a cache miss.
  • 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_images when serving images for product 123, this command purges all cached items associated with that tag.
  • 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/.

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.

Want structured learning?

Take the full Caching-strategies course →