CDNs and Caching Explained: The Cheapest Way to a Faster Website
Caching is the cheapest performance upgrade most sites never set up properly. Here is how CDNs, browser caches and server caches work, what to cache, and what should never be cached.

If you want a faster website on a small budget, caching is where I would spend it. It is usually a configuration change rather than a rebuild, and done well it makes a site faster for every visitor while lowering hosting costs. Done badly it serves yesterday's prices or, worse, one customer's account page to another. Here is how it works, what to cache, and how to check what your site is doing today.
Caching in one sentence
A cache is a saved copy of an answer, kept somewhere closer or cheaper than the original, so the work does not have to be done again. Every layer of caching on a website is a variation on that idea, and the questions are always the same: where is the copy kept, how long is it trusted, and how do you replace it when the original changes?
The layers between your server and your visitor
- Browser cache. The visitor's own browser keeps files it has already downloaded — images, stylesheets, scripts — so the next page view does not fetch them again.
- CDN cache. A content delivery network keeps copies of your files, and often whole pages, in data centres around the world. Visitors get the copy nearest to them.
- Page cache on the server. Your server saves finished pages so it does not rebuild them from the database on every request. On WordPress this is what caching plugins and many managed hosts do.
- Object and data cache. Deeper in the stack, the results of expensive database queries or API calls are kept in memory for reuse.
Each layer that answers a request saves the layers behind it from doing any work. A request answered by the browser cache costs your server nothing at all.
What a CDN actually does
A CDN sits in front of your server. When a visitor requests a file, the CDN location nearest to them checks whether it has a fresh copy. If it does, it serves it immediately: a cache hit. If not, it fetches the file from your server, passes it on, and keeps a copy for the next person: a cache miss.
The benefits stack up. Visitors far from your server get files from nearby. Your server handles a fraction of the traffic, so a spike from a campaign or a press mention is far less likely to take it down. Most CDNs also handle compression, modern TLS and some protection against abusive traffic. Faster pages pay back in more ways than one, which I cover in the hidden ROI of a fast website.
Many platforms include a CDN already; Vercel, Netlify and Shopify all put one in front of your site by default. If yours does not, it is one of the first things I check when choosing hosting. A CDN that caches pages is not the same as running code at the edge, a distinction I unpack in edge rendering explained.
Cache-Control in plain English
Your server tells browsers and CDNs how to cache each response using the Cache-Control header. You do not need to memorise it, but you should recognise the main instructions.
max-age=3600: caches may reuse this for 3,600 seconds (one hour) without checking back.s-maxage=86400: shared caches such as a CDN may keep it for a day. Browsers ignore this and usemax-age.stale-while-revalidate=60: once the copy expires, a cache may keep serving it for up to 60 more seconds while it fetches a fresh one in the background, so visitors never wait for the refresh. Support varies between CDNs, so check yours.immutable: this file will never change at this URL, so do not even check.private: only the visitor's own browser may keep it, never a shared cache like a CDN.no-store: do not keep a copy anywhere. For anything personal or sensitive.
The rule for hashed assets
Modern build tools give CSS and JavaScript files names that include a fingerprint of their contents, something like app.3f9a1c.js. When the file changes, the name changes. The old URL will never serve different content, so it can be cached for as long as possible:
Cache-Control: public, max-age=31536000, immutable
That is one year, and it is safe precisely because a new release uses new file names. Frameworks like Next.js set this on their build output automatically. The mistake I see is the same header applied to files without a fingerprint, such as /logo.png or /styles.css, which then refuse to update when you change them.
What to cache, and what never to
| Content | Cache it? | Typical approach |
|---|---|---|
| Fingerprinted CSS, JS and fonts | Yes, for a long time | One year, immutable |
| Images and media | Yes | Long cache; rename or version the URL when replaced |
| Marketing pages and blog posts | Yes, at the CDN | Short browser cache, longer CDN cache, stale-while-revalidate |
| Product and category pages | Yes, carefully | Short CDN cache or purge on update, so prices and stock stay accurate |
| Basket, checkout and account pages | Never in a shared cache | private or no-store |
| Anything behind a login or showing personal data | Never in a shared cache | no-store |
| Form submissions and API writes | No | Not cacheable |
The dangerous mistake is caching a personalised page at the CDN. If a page includes a name, a basket or an order history and it gets cached as a shared page, the next visitor can see it. That is a data breach, not a performance bug, and it belongs on any website security checklist. When in doubt, do not share-cache pages that change depending on who is logged in.
Cache invalidation: the hard part
Caching is easy. Knowing when to throw a copy away is the hard part. There are four common strategies, and most sites use a mix.
- Versioned URLs. Change the file name when the content changes. The cleanest option for assets.
- Short lifetimes with stale-while-revalidate. Pages refresh every few minutes in the background. Simple, and fine for most content.
- Purge on publish. When an editor updates a page, the CMS or host tells the CDN to drop that URL. Most good hosts and WordPress caching plugins can do this automatically.
- Tag-based revalidation. Frameworks like Next.js let you label cached data, then refresh everything carrying a tag — every page showing a particular product, say — when that product changes.
If your team has ever said "the change is live but I cannot see it", your invalidation strategy is the thing to fix, not your staff's patience.
How to check your own site
You do not need to be a developer to see what your site is doing.
- Open your site in Chrome, open DevTools and go to the Network tab.
- Reload the page, click the main document request, and look at the response headers.
- Find
cache-control, and look for anageheader, which shows how many seconds a shared cache has held the copy. - Look for a cache status header from your CDN. The name varies by provider:
cf-cache-statuson Cloudflare,x-vercel-cacheon Vercel, oftenx-cacheelsewhere.HITmeans it came from the cache.
Developers can do the same from a terminal:
curl -I https://www.example.com/
If your homepage shows no-store or a permanent MISS, every visitor is waiting for your server to build it from scratch. That is common on WordPress sites without page caching, one of the causes in why your WordPress site is slow.
What caching will not fix
Caching makes pages arrive faster. It does nothing for a page that arrives quickly and then freezes under heavy JavaScript; that is an interactivity problem, covered in how to fix INP. It also will not rescue an oversized hero image, which will simply be delivered faster from somewhere nearby. Fix the weight first, then cache the result.
Want to know what your site is caching, and what it should be? Send me the URL and I will check the headers and tell you what I would change first.




