⏱️ Lectura: 13 min

Netflix doesn’t send every show from a data center in California: it copies it thousands of times into metal boxes installed directly inside your internet provider’s routers, a project the company calls Open Connect. That’s essentially what a CDN is: a network of servers that brings content closer to the user instead of making every bit travel from a single origin.

📑 En este artículo
  1. TL;DR
  2. What a CDN is and why it matters
  3. How a CDN works under the hood
  4. Anycast: how the same IP responds from the right place
  5. Practical examples
  6. How to get started step by step
  7. Real-world use cases
  8. Common mistakes and best practices
  9. Comparison with alternatives
  10. Going deeper (advanced)
  11. Frequently Asked Questions
    1. Is a CDN only for static files?
    2. What’s the difference between a CDN and a reverse proxy?
    3. How do I invalidate the cache when I update content?
    4. Does a CDN improve SEO?
    5. Do I need a CDN if my site is small?
    6. What is a cache stampede and how is it avoided?
  12. References

Once you understand how a CDN works (what it caches, how it decides which server responds, and when that copy expires), you can speed up any site, whether it’s a personal blog or an application with millions of users. This article explains the whole mechanism, with real commands and configuration you can try today.

TL;DR

  • You’ll understand what happens when you request a page: cache hit, cache miss, and TTL explained step by step.
  • You’ll be able to read the Cache-Control, Age, and X-Cache-Status headers of any site with curl.
  • You’ll know how to configure proxy_cache in Nginx to serve cached content from your own proxy.
  • You’ll understand why anycast lets the same IP respond from the point closest to the user.
  • You’ll be able to write a Cloudflare Worker that manually caches responses with the Cache API.
  • You’ll identify and avoid a cache stampede when many users request the same expired resource.
  • You’ll know how to choose between Cloudflare, CloudFront, Fastly, or your own proxy based on your budget and scale.

What a CDN is and why it matters

A CDN (Content Delivery Network) is a set of geographically distributed servers that store copies of the same content and deliver it from the point closest to whoever requests it. Instead of every visit traveling to the original server (the origin), most requests are resolved at an intermediate server called an edge node or point of presence (PoP).

The reason a CDN exists is physical: light takes a minimum amount of time to travel through a fiber optic cable, and that time multiplies with the number of network hops between the user and the server. A user in Buenos Aires requesting a file hosted in Virginia passes through several transit providers before getting a response. If that same file is cached at a closer PoP, the distance (and the latency) drops drastically.

But a CDN isn’t just about speed. It also absorbs traffic spikes: if ten thousand users request the same image, the edge serves it from cache without touching the origin even once. It also reduces the bandwidth you pay for on your origin server and acts as a first line of defense against denial-of-service attacks, because traffic gets filtered before it reaches the real backend.

Edge servers of a CDN distributed geographically
Each point of presence keeps a local copy of the most requested content. Foto de Franck V. en Unsplash

How a CDN works under the hood

The core mechanism is HTTP caching. When a browser requests a resource, the server can respond with the Cache-Control header, which tells any intermediary (the browser itself, a proxy, or the CDN edge) how long it can keep that response before requesting it again. That time is called TTL (time to live).

The first time an edge receives a request for a resource it doesn’t have stored, a cache miss occurs: the edge repeats the request against the origin, stores the response according to the given TTL, and delivers it to the user. The next time someone requests the same thing, as long as the TTL hasn’t expired, a cache hit occurs: the edge responds directly from its local copy, without contacting the origin.

sequenceDiagram
    participant U as User
    participant E as Edge CDN
    participant O as Origin
    U->>E: requests /image.jpg
    alt cache miss
        E->>O: requests the resource
        O-->>E: returns the file
        E-->>U: delivers and stores a copy in cache
    else cache hit
        E-->>U: responds from local cache
    end

The diagram shows both paths. In the first, the edge acts as a transparent intermediary and pays the latency cost only once for all the users who request that resource while the TTL lasts. In the second, the edge resolves everything locally, with no round trip to the origin.

Anycast: how the same IP responds from the right place

A large CDN doesn’t have a single IP address per server: it uses anycast, a routing technique where the same IP address is announced from dozens or hundreds of different locations via the BGP protocol. The internet’s network of routers decides, at any given moment, which of those announcements is closest in terms of network hops, and routes the user’s request to that point.

This is what lets an IP like 1.1.1.1 respond in milliseconds no matter which country it’s queried from: there isn’t a single server with that address, there are hundreds, and BGP picks the shortest path each time without the client application needing to know anything about it.

flowchart TD
    A["User in Lima"] --> B["Nearest PoP: Bogota"]
    C["User in Madrid"] --> D["Nearest PoP: Madrid"]
    B --> E[("Origin: central server")]
    D --> E
    subgraph "CDN Network"
    B
    D
    end

Practical examples

These four examples go from the simplest (inspecting headers with curl) to a real case of programmable caching at the edge.

The first one doesn’t require installing anything: it lets you see caching in action on any site that already uses a CDN.

curl -I https://developer.mozilla.org/

The -I flag requests only the headers. Look for cache-control, age (seconds since the edge stored the copy), and, if the site uses Cloudflare or Fastly, cf-cache-status or x-cache with values like HIT or MISS.

The second example controls the TTL from your own origin server, something any CDN respects:

const express = require('express');
const app = express();

app.use('/static', express.static('public', {
  maxAge: '7d',
  setHeaders: (res) => {
    res.setHeader('Cache-Control', 'public, max-age=604800, immutable');
  }
}));

app.listen(3000);

This code serves the public folder with a Cache-Control of seven days and the immutable directive, which tells the browser and the CDN that this file will never change while it keeps that name (useful for assets with a hash in the name, like app.a1b2c3.js).

Console showing HTTP cache control headers
The Age header shows how long the copy has been living at the edge. Foto de Markus Winkler en Unsplash

The third example spins up a proxy with its own cache using Nginx:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=static_cache:10m max_size=1g inactive=60m use_temp_path=off;

server {
    listen 80;
    location / {
        proxy_cache static_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_pass http://origin_backend;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

This configuration caches 200 and 302 responses for 10 minutes, and 404 errors for 1 minute (so it doesn’t keep hitting the origin constantly for a broken URL). The X-Cache-Status header exposes the result in every response.

The fourth example goes a step further: programmable caching at the edge with a Cloudflare Worker:

addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const cache = caches.default;
  let response = await cache.match(request);
  if (!response) {
    response = await fetch(request);
    response = new Response(response.body, response);
    response.headers.append('Cache-Control', 'max-age=3600');
    event.waitUntil(cache.put(request, response.clone()));
  }
  return response;
}

Here the code itself decides what to cache and for how long, something impossible with a traditional CDN configured only through headers. This is the foundation of what’s known as edge compute.

How to get started step by step

You can reproduce a CDN’s behavior on your machine with two containers: one that simulates the origin and another that simulates the caching edge.

docker run -d --name origin -p 8080:80 nginx
docker run -d --name cdn-cache -p 80:80 -v $(pwd)/nginx-cache.conf:/etc/nginx/conf.d/default.conf nginx
curl -I http://localhost/index.html
curl -I http://localhost/index.html

The nginx-cache.conf file is the proxy_cache configuration from the previous example, pointing proxy_pass to http://origin:80. The first curl call will show X-Cache-Status: MISS (it went to the origin). The second will show HIT, served entirely from the cdn-cache container without touching the origin. That’s the concrete proof that caching is active: you don’t have to take it on faith, it’s verified right in the header.

Real-world use cases

Static sites (blogs, documentation, landing pages) are the simplest case: almost all the content can have a long TTL because it rarely changes. Video streaming, like the Netflix case mentioned earlier, uses the same principle at a different scale: the most-watched episodes get replicated across thousands of appliances inside internet provider networks so the backbone links don’t get saturated.

APIs benefit too, though carefully: public, non-personalized responses (a product catalog, exchange rates) can be cached for seconds or minutes, while responses containing session data should never go through a shared cache. Package repositories (npm registries, Docker Hub images) also rely on CDNs to avoid collapsing when thousands of CI builds download the same dependency at once.

Common mistakes and best practices

The most dangerous mistake is caching a response that depends on user data (a session cookie, a token) without declaring it. If the edge doesn’t know the response varies based on that data, it can serve one user another user’s personalized content: this is known as cache poisoning caused by headers not accounted for in the cache key.

⚠️ Careful: if your response changes based on a cookie or an authorization header, add that header to the Vary directive or exclude the route from caching entirely. Otherwise, the first user who requests that URL determines what everyone else sees for as long as the TTL lasts.

Another classic problem is the cache stampede: when the TTL of a very popular resource expires, dozens of simultaneous requests hit the edge, all find a miss at once, and all hit the origin at the same time, as if the cache didn’t exist. The usual solution is request coalescing: the edge lets only one request through to the origin and makes the rest wait (or serves them a stale copy) until the fresh response arrives.

💡 Tip: the Cache-Control stale-while-revalidate directive solves this declaratively: the edge serves the expired copy instantly while revalidating in the background, instead of leaving everyone waiting.

Finally, a common oversight in deployments: changing the origin’s content without purging the CDN cache. If the HTML has a one-hour TTL and you push an urgent deploy, that hour still runs out unless you trigger a manual purge through the provider’s API.

Comparison with alternatives

OptionWhen to use itAdvantageLimitation
Cloudflare (free plan)Blogs and small to medium sitesFree, global anycast network, basic DDoS protection includedAdvanced purge and cache rules require a paid plan
AWS CloudFrontApps already living on AWSDirect integration with S3 and Lambda@EdgeBilling per region and per function is harder to estimate
FastlyHigh traffic with a need for instant purgingMillisecond purging, low-level configurable VCLSteeper VCL learning curve
Self-hosted Nginx or VarnishFull control, no dependency on a SaaSNo vendor lock-in, 100% of the config in your handsYou operate the infrastructure, no real global network
No CDN (origin only)Internal apps or very low trafficSimplicity, a single point to maintainHigh latency for distant users, no resilience against spikes

Going deeper (advanced)

The composition of the cache key is what breaks most often in production. By default, many CDNs cache by full URL including query strings, which means /producto?ref=twitter and /producto?ref=facebook generate two separate entries for the same content. Adjusting the key to ignore tracking parameters is a common optimization that multiplies the hit ratio without changing a single line of backend code.

Negative caching (also caching error responses, as seen in the Nginx example with 404s for 1 minute) prevents a nonexistent resource from generating a storm of requests to the origin every time someone requests it by mistake or because of a misconfigured bot.

The largest CDNs also use a multi-tier architecture: an edge PoP close to the user, and behind it an intermediate layer called an origin shield that groups misses from many different PoPs before they reach the real origin. That way, even with a hundred points of presence around the world, the origin only sees a fraction of the total miss traffic.

📖 Summary on Telegram: View summary

Your next step: spin up the two containers from the Docker example, run curl twice in a row, and confirm with your own eyes the change from MISS to HIT in X-Cache-Status.

Frequently Asked Questions

Is a CDN only for static files?

No. While static files are the simplest case, a CDN can also cache public API responses, do edge compute with Workers or Lambda@Edge, and act as a reverse proxy for all traffic, including dynamic content you choose not to cache.

What’s the difference between a CDN and a reverse proxy?

A reverse proxy (like plain Nginx with no extra configuration) forwards requests to a backend. A CDN is a network of many caching reverse proxies, geographically distributed and using anycast routing, specifically designed to bring content closer to the end user.

How do I invalidate the cache when I update content?

With the provider’s purge API (by URL, by tag, or a full purge), or by designing versioned URLs (with a hash in the file name) that never need purging because each version has its own address.

Does a CDN improve SEO?

Indirectly, yes: search engines factor page load speed into ranking, and reducing latency with a CDN usually improves metrics like Largest Contentful Paint.

Do I need a CDN if my site is small?

It’s not mandatory, but even Cloudflare’s free plans provide basic protection against traffic spikes and attacks, plus the latency improvement, without any extra infrastructure cost.

What is a cache stampede and how is it avoided?

It’s when many simultaneous requests find the same expired resource and all hit the origin at once. It’s avoided with request coalescing (only one request goes through, the rest wait) or with the stale-while-revalidate directive.

References

📱 Enjoy this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.

Imagen destacada: Foto de Tyler en Unsplash


Javier Alarcón

Infrastructure engineer specializing in networking, Linux systems, Kubernetes, and cloud architectures. Covers hardware, networking, observability, and engineering practices for production teams.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.