# CDN Integration Low-Level Design

Source: https://www.techinterview.org/post/3233468063/lld-cdn-integration/
Updated: 2026-04-17 · techinterview.org

## CDN Integration — Low-Level Design

A CDN (Content Delivery Network) caches static assets and API responses at edge nodes close to users, reducing latency and origin server load. Integrating a CDN correctly requires designing cache keys, cache headers, invalidation, and edge behavior. This design is asked at Netflix, Cloudflare, and any globally distributed platform.

## What to Put Behind a CDN


```
Excellent CDN candidates (high cache hit ratio):
  - Static assets: JS, CSS, fonts, images — cache for 1 year (immutable)
  - Video/audio segments (.ts, .m4s, .mp3) — cache for 1 year
  - API responses that are the same for all users (public catalog, homepage data)
  - Resized image variants (once generated, never change)

Poor CDN candidates:
  - User-specific responses (cart, profile, notifications) — must not be shared
  - Responses with Set-Cookie or Authorization headers — CDN caches per-user key
  - Highly dynamic data (real-time prices, stock levels) — TTL must be very short
  - Write operations (POST, PUT, DELETE) — should bypass CDN to origin
```


## Cache-Control Headers


```
from flask import make_response

# Immutable assets (content-hashed filenames):
# /static/app.abc123.js — hash changes when content changes
@app.route('/static/')
def static_file(filename):
    response = make_response(send_file(filename))
    response.headers['Cache-Control'] = 'public, max-age=31536000, immutable'
    # immutable: browser never revalidates (no conditional request)
    return response

# API response: cache at CDN for 60 seconds, client for 10 seconds
@app.route('/api/public/products')
def public_products():
    products = get_public_products()
    response = make_response(jsonify(products))
    response.headers['Cache-Control'] = 's-maxage=60, max-age=10, stale-while-revalidate=30'
    # s-maxage: CDN TTL; max-age: browser TTL; stale-while-revalidate: serve stale while refreshing
    return response

# Never cache:
@app.route('/api/user/cart')
def user_cart():
    response = make_response(jsonify(get_cart()))
    response.headers['Cache-Control'] = 'private, no-store'
    return response
```


## Cache Keys and Vary Headers


```
-- Default CDN cache key: URL only (scheme + host + path + query)
-- Problems arise when the same URL returns different content based on headers

-- Accept-Encoding: client supports gzip → CDN must cache compressed and uncompressed separately
Vary: Accept-Encoding  ← tells CDN to include Accept-Encoding in cache key

-- Accept: image/webp → CDN must cache WebP and JPEG variants separately
Vary: Accept  ← for content negotiation

-- Cookie or Authorization → user-specific, must not be shared
-- WRONG: setting Vary: Cookie on public endpoints — every user gets their own cache entry
-- RIGHT: strip auth cookies before caching, or set Cache-Control: private

-- CloudFront example: custom cache policy
Cache key:
  URL path + query string (selected params only)
  NOT: cookies, Authorization header (these cause per-user cache fragmentation)
```


## Cache Invalidation


```
-- Option 1: Versioned URLs (best for static assets)
-- Bundle hashes filenames: app.abc123.js → app.def456.js on next deploy
-- No invalidation needed; old URL serves old file, new URL serves new file

-- Option 2: CDN API invalidation (for content changes)
def invalidate_cdn_cache(paths):
    cloudfront.create_invalidation(
        DistributionId='E1234567890ABC',
        InvalidationBatch={
            'Paths': {'Quantity': len(paths), 'Items': paths},
            'CallerReference': str(uuid.uuid4()),
        }
    )

# Examples:
invalidate_cdn_cache(['/api/public/products'])      # Specific endpoint
invalidate_cdn_cache(['/images/product-123/*'])     # Image variants for a product

# CloudFront: $0.005 per 1,000 invalidation paths
# Use wildcards to reduce cost: /images/product-123/* counts as 1 path

-- Option 3: Short TTL + stale-while-revalidate (for frequently changing data)
Cache-Control: s-maxage=30, stale-while-revalidate=60
-- CDN serves stale (up to 60s old) while fetching fresh in background
-- No explicit invalidation needed for data that changes frequently
```


## Edge Functions (Compute at CDN)


```
// CloudFront Functions / Cloudflare Workers: run JS at every edge node

// Example: A/B test routing at the edge (no origin hit)
export default {
  async fetch(request) {
    const url = new URL(request.url);
    const userId = getCookie(request, 'user_id');
    const bucket = parseInt(userId) % 2;  // 50/50 split

    if (bucket === 0) {
      url.pathname = url.pathname.replace('/checkout', '/checkout-v2');
    }
    return fetch(url.toString(), request);
  }
};

// Example: Add security headers at edge
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('Content-Security-Policy', "default-src 'self'");
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
```


## Key Interview Points

- **Content-addressed filenames eliminate cache invalidation for static assets:** app.abc123.js will never change. Set max-age=31536000, immutable and never think about invalidation for that file. The build process handles rotation by generating new hashes.

- **Vary: Cookie destroys CDN efficiency:** If you set Vary: Cookie on a public endpoint, every user gets their own cache entry. The cache hit rate drops to near zero. Strip or ignore auth cookies before caching public responses.

- **stale-while-revalidate enables smooth cache refresh:** Without it, when TTL expires, the first request waits for origin. With stale-while-revalidate, the stale response is served immediately while the CDN refreshes in background. Users see no latency spike at TTL expiry.

- **CDN logs enable origin request reduction metrics:** The key CDN metric is cache hit ratio (target: >95% for static, >70% for cacheable API responses). Monitor hit ratio per path; a low-hit path signals a misconfigured Cache-Control header.

CDN integration and global content delivery design is discussed in [Netflix system design interview questions](https://www.techinterview.org/?p=3233460282).

CDN integration and CloudFront delivery design is covered in [Amazon system design interview preparation](https://www.techinterview.org/post/3233460275/scale-ai-interview-guide-2026-data-infrastructure-rlhf-pipelines-and-ml-engineering/).

CDN integration and media delivery optimization is discussed in [Snap system design interview guide](https://www.techinterview.org/post/3233459923/snap-interview/).
