---
title: Edge caching
summary: A CDN can serve your pages from a city near the visitor in milliseconds, or serve one person's account page to thousands of strangers. The headers decide which.
topic: infrastructure
order: 1
updated: 2026-09-19
---

## What it is

A **CDN** (content delivery network) such as Cloudflare keeps copies of your responses in
data centres around the world, called the **edge**. When a visitor in Manila asks for a page,
the copy in Manila answers, and your server never hears about it.

What the edge may keep, and for how long, is decided by one header on your response:
`Cache-Control`.

## Why it is a rule

- **Steam, Christmas Day 2015.** Under a heavy attack, Valve changed its caching setup. For
  about an hour, account pages were cached and served to other people: names, addresses, the
  last digits of cards and purchase history. Valve later said around 34,000 users had their
  details shown to someone else. Nothing was hacked. A page meant for one person had been
  marked as safe to share.

The lesson: caching a page that varies by person is not a performance bug. It is a data
breach.

## How to do it

### Know the directives

| Directive | Meaning |
| --- | --- |
| `public` | Any cache may keep it, including the CDN |
| `private` | Only the visitor's own browser may keep it |
| `no-store` | Nobody keeps it, ever |
| `no-cache` | Keep it, but check with the server before each use |
| `max-age=N` | The browser may use it for N seconds |
| `s-maxage=N` | Shared caches (the CDN) may use it for N seconds; overrides `max-age` there |
| `immutable` | This URL's content will never change; do not even revalidate |

### Sort every response into one of three kinds

```text
Same for everyone, changes on deploy   →  public, max-age=0, s-maxage=31536000
Same for everyone, never changes        →  public, max-age=31536000, immutable
Different per person, or has a secret   →  private or no-store
```

- **Marketing pages and articles** go in the first group. The CDN keeps them for a long
  time; browsers always ask again, so a deploy shows up at once.
- **Versioned assets** (`app.js?v=abc123`) go in the second. A new deploy uses a new URL, so
  the old one can be cached forever. See the deploys lesson.
- **Anything that reads a cookie or a session** goes in the third, and APIs are `no-store`.

### Purge on deploy

A long `s-maxage` is only safe if every deploy clears the edge. The new server purges the CDN
as it boots, so no visitor sees an old page after a release.

### Keep the list short and explicit

Decide per page, in one place, which paths may be edge-cached. Everything else defaults to
not cached. A page added later is then safe by default.

## How we do it here

Only pages that are identical for every visitor are edge-cached: the home page, About, the
legal pages and these lessons. They are cached at Cloudflare until the next deploy, which
purges the cache when the new server starts. Anything that reads a session, and every API
response, is never stored at the edge.

## Benefits

- Pages load from a nearby city, often in tens of milliseconds.
- The origin server does almost no work for public traffic, and survives spikes.
- The CDN keeps serving the public pages even while the origin restarts.

## Disadvantages

- **One wrong header is a breach**, not a slowdown, as Steam showed.
- Purging depends on the CDN's API. If the purge fails, visitors see the old site until it
  succeeds.
- Debugging gets harder: "it works on my machine" can mean "the edge still has yesterday's
  copy".

## Checklist

- Every edge-cached path is listed explicitly.
- Nothing that reads a cookie or session is on that list.
- APIs send `no-store`.
- Each deploy purges the edge, and a failed purge is logged loudly.

## Sources

- [MDN — Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)
- [RFC 9111 — HTTP Caching](https://www.rfc-editor.org/rfc/rfc9111)
- Valve, statement on the Steam caching incident of 25 December 2015.
