Featured image of post CDN: The 'Local Convenience Store' Mental Model

CDN: The 'Local Convenience Store' Mental Model

Why does your image load instantly for users in the US but crawls in Vietnam? A mastery guide to CDN Edge Nodes, Cache-Control headers, and cache busting.

Your product photos are hosted on a server in Frankfurt. A user in Hanoi clicks your website. That image has to physically travel 9,000 km of fiber optic cable underwater before it appears on their screen.

Physics is the bottleneck. You cannot make light move faster.

But you can move the content closer to the user. This is a CDN.

This is the Mastery Guide to Content Delivery Networks — from the mental model to Cache-Control headers to cache busting.


Part 1: Foundations (The Mental Model)

The Origin Server = The Main Factory

Your server in Frankfurt is the Main Factory. It produces everything: your app, your images, your videos, your CSS files.

The problem: your factory is far away. Shipping from Frankfurt to Hanoi takes time (150–300ms round-trip just for the network alone).

The CDN Edge Node = The Local 7-Eleven

A CDN (like Cloudflare or AWS CloudFront) operates hundreds of Edge Nodes — servers placed in cities around the world.

Think of each Edge Node as a local 7-Eleven convenience store. It doesn’t produce anything; it just stocks the most popular items from the Main Factory nearby.

1
2
3
4
5
6
7
8
User in Hanoi
CDN Edge (Singapore) ← Stocks copy of your images/assets
      │ (10ms away)
      │   if not cached yet ↓
      └──────────────────► Origin Server (Frankfurt)
                           (300ms away — only for first request!)

The Key Insight: The first user in each region hits the origin. Every subsequent user in that region gets served from the local Edge Node. The origin server is only called once per region per file.


Part 2: The Investigation (What Gets Cached)

Not everything should be on a CDN. Only static, infrequently-changing content.

Should CDN Cache?ExamplesWhy
YesImages, CSS, JS bundles, Fonts, VideosSame file for every user.
YesPublicly-accessible API responsesSame JSON for every user.
NoUser-specific pages (/dashboard)Different per user.
NoAuthentication endpoints (/login)Cannot be cached!
NoReal-time data (/stock-price)Changes every second.

The Cache-Control Header

You instruct the CDN (and the browser) how long to cache a file using the Cache-Control HTTP response header.

1
Cache-Control: public, max-age=31536000, immutable
  • public: “Intermediaries (CDN) can cache this.”
  • max-age=31536000: “Cache for 1 year (in seconds).”
  • immutable: “This file will NEVER change. Don’t even bother checking for updates.”

The Golden Rule for Static Assets: Use long TTLs + cache busting via filename hashing.


Part 3: The Diagnosis (Cache Busting — “My Update Isn’t Showing!”)

You uploaded a new logo.png. But users keep seeing the old one for days because the CDN cached it.

This is the #1 CDN frustration. The fix is Cache Busting.

The Wrong Way (Purging)

You manually go to Cloudflare and click “Purge Cache” every time you deploy. This is:

  • Easy to forget.
  • Only clears the CDN; the browser might still have it cached.

The Right Way (Content Hashing)

Name your files with a hash of their content. When the content changes, the filename changes. Caches are busted automatically.

1
2
3
4
5
# Old file:
logo.abc123.png   Cache-Control: max-age=31536000

# You update the logo. Build tool detects change.
logo.def456.png   New URL! CDN & browsers see this as a BRAND NEW file.

Modern build tools (Vite, Webpack) do this automatically. Your <img src="logo.abc123.png" /> becomes <img src="logo.def456.png" /> after every build where the file changes.


Part 4: The Resolution (Setup Patterns)

1. Cloudflare (Simplest Setup)

Change your DNS to point to Cloudflare. Every HTTP request passes through their global network automatically. Static assets are cached at the edge.

Pro tip: Set a Page Rule to cache everything with a long TTL for your static assets path.

2. AWS CloudFront + S3

Host static assets in S3. Put CloudFront in front of it.

1
User → CloudFront (Edge) → S3 (Origin)

Correct Cache-Control header in S3: When you upload a file, set the metadata:

1
Cache-Control: public, max-age=31536000

3. Nginx as a “Mini CDN” for your origin

Cache responses at your Nginx layer before they hit your app:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
proxy_cache_path /tmp/nginx_cache levels=1:2
                 keys_zone=my_cache:10m max_size=1g;

server {
    location /static/ {
        proxy_cache        my_cache;
        proxy_cache_valid  200 1y;    # Cache 200 responses for 1 year
        add_header         X-Cache-Status $upstream_cache_status;  # HIT or MISS
        proxy_pass         http://backend;
    }
}

Final Mental Model

1
2
3
4
5
6
7
CDN Edge Node -> The local 7-Eleven. (Nearby, fast, stocks popular items).
Origin Server -> The Main Factory. (Far, authoritative, visited rarely).

Cache-Control: max-age=31536000 -> "Keep this for exactly 1 year."
Content Hashing (logo.abc123.png) -> "New filename = cache busted automatically."
X-Cache-Status: HIT -> "Served from Edge. User is happy."
X-Cache-Status: MISS -> "Served from Origin. User waited."

Rules:

  • If it’s the same for every user → put it on a CDN.
  • If it changes → use content hashing, not manual purging.
  • If it’s private → never put it on a CDN.
Made with laziness love 🦥

Subscribe to My Newsletter