---
title: "CDN: The 'Local Convenience Store' Mental Model"
description: "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."
pubDate: "2023-05-25"
lang: "en"
slug: "cdn-content-delivery-network"
url: "https://hoangyell.com/cdn-content-delivery-network/"
author: "HoangYell"
translation_vi: "https://hoangyell.com/vi/cdn-content-delivery-network/"
categories: ["System Design"]
tags: ["system-design", "performance", "networking"]
heroImage: "https://raw.githubusercontent.com/HoangGeek/store/main/webp/photo-1573804633927-bfcbcd909acd.webp"
---

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.

```
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? | Examples | Why |
|:---|:---|:---|
| ✅ **Yes** | Images, CSS, JS bundles, Fonts, Videos | Same file for every user. |
| ✅ **Yes** | Publicly-accessible API responses | Same JSON for every user. |
| ❌ **No** | User-specific pages (`/dashboard`) | Different per user. |
| ❌ **No** | Authentication endpoints (`/login`) | Cannot be cached! |
| ❌ **No** | Real-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.

```
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.

```
# 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 `` becomes `` 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.

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

**Correct `Cache-Control` header in S3**: When you upload a file, set the metadata:
```
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:

```nginx
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

```
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.
