← All lessons
Performance delivery
Intermediate
20-30 min

Caching, CDN, and Data Freshness for Frontend Leads

How to make an app feel instant without showing stale data: a practical guide to browser cache, edge cache, and revalidation policy.

Learn how cache layers work together, what should be cached, and how to balance speed with freshness so users get a fast experience without trust-breaking stale data.

Why this matters
What you gain from this lesson
Caching is one of the cheapest ways to make a product feel faster, but the wrong cache policy can show the wrong answer with confidence.
A frontend lead needs to know when to use browser cache, CDN cache, revalidation, or a strict no-store path.
Good cache decisions reduce backend load, protect user trust, and make UI trade-offs easier to explain to product and engineering partners.
At a glance
Lesson map
Focus
Performance delivery
Outcome
Better decisions
This lesson is designed to stay readable on mobile: the basics are concise, the takeaways are highlighted, and the visual helps you see how the trade-offs fit together.

Core concepts

Speed and freshness are always in tension
A fast app that shows outdated data is still broken. The real goal is not maximum caching; it is choosing how much staleness a user can tolerate for each screen.

The first job of caching is perception. If the page appears instantly, the user feels progress even before the freshest data arrives.

The second job is correctness. Some screens can tolerate a little staleness, but user-specific or sensitive data usually cannot.

  • Public content can usually tolerate more caching than private content.
  • The more important the freshness, the tighter the cache policy should be.
  • A lead should treat freshness as a product rule, not a guess.
Every cache layer has a different job
Browser cache, CDN cache, app cache, and origin cache solve different problems. Mixing them up makes debugging harder and can hide the real source of stale data.

Browser cache helps one user reuse assets and responses locally. CDN cache helps many users in nearby regions get the same public content quickly. Origin cache reduces pressure on the application server, while app cache often manages data inside the client runtime.

When you understand which layer answered the request, it becomes much easier to explain why the page was fast, why the data was stale, or why a refresh still had to hit the backend.

  • Browser cache is best for static assets and repeatable responses.
  • CDN cache is best for shared public content and traffic spikes.
  • Private or session-bound data should usually avoid edge caching.
Freshness is controlled by headers and versioning
Caching is not all-or-nothing. Headers and versioned filenames let you say exactly how long a response can live and when it must be checked again.

`Cache-Control` is the main policy switch. `max-age`, `s-maxage`, `no-cache`, `no-store`, and `stale-while-revalidate` each describe a different balance between speed and correctness.

`ETag` and `Last-Modified` help the client ask, 'Has anything changed?' For static assets, versioned filenames are often the cleanest way to avoid accidental stale content after deploys.

  • Use `no-store` when the response should never be persisted.
  • Use revalidation when the user can tolerate a short period of staleness.
  • Versioned file names are safer than manual cache clearing for static assets.
Choose policy by content type, not by habit
The right cache rule depends on what the screen shows, how often the data changes, and how wrong the data can be before users notice.

A product list can often be cached for minutes, while a cart, notification badge, or payment status needs much tighter control. The key idea is to match the policy to the risk.

For many product screens, the best pattern is fast initial render plus background refresh. That gives users speed first and freshness shortly after, which is often better than blocking the screen until the backend returns perfect data.

  • Stable shared data can be cached aggressively.
  • User-specific state should be protected more carefully.
  • The freshness policy should be decided before the screen ships.

Visual model

Speed and freshness are a policyCache the stable stuff, revalidate the changing stuff, and protect the private stuff.Useropens a page, taps refresh, or navigates backrequest starts hereBrowser cachestatic assets, icons, images, and reusable API responsesbest when the content is stable and sharedfast hitCDN / edgepublic pages, product images, and shared API dataserves nearby users faster and absorbs traffic spikesuse `stale-while-revalidate` when short staleness is acceptablerevalidate pathOrigin serverprivate data, computed responses, and anything that must be currentthis is the source of truth when cache misses happenorigin hitDatabaseonly when fresh data must be read or writtenFast path = cache hit • Accurate path = revalidate • Private data = strict control

Practical examples

Static assets should be aggressively cacheable
Versioned assets can live for a long time because the filename changes when the file content changes.
Request or current behavior
GET /_next/static/chunks/app-shell.4f92a.js
Better response
200 OK
Cache-Control: public, max-age=31536000, immutable

{ ...built asset bytes... }
Lesson: If the filename is versioned, the browser can keep the file for a long time without risking stale code after deploys.
Catalog pages can revalidate in the background
A public listing can be served fast even if it is slightly stale, as long as the system refreshes it soon after.
Request or current behavior
GET /api/products?category=chairs
Better response
200 OK
Cache-Control: public, s-maxage=300, stale-while-revalidate=60

{
  "items": [
    { "id": "p_1", "name": "Oak chair" }
  ]
}
Lesson: The user sees something quickly, and the cache can refresh in the background instead of forcing a slower full miss every time.
Private state should stay strict
Cart data and sensitive account data should not be cached like a public catalog page.
Request or current behavior
GET /api/cart
Better response
200 OK
Cache-Control: no-store

{
  "items": [
    { "id": "sku_1", "qty": 2 }
  ]
}
Lesson: When the data is user-specific or security-sensitive, freshness and safety matter more than edge speed.
ETag lets the client ask before downloading again
Revalidation avoids re-sending the whole response when the content has not changed.
Request or current behavior
GET /api/profile
If-None-Match: "profile_123_v8"
Better response
304 Not Modified
Lesson: Conditional requests are a nice middle ground: the app checks freshness without always re-downloading the full payload.

Common pitfalls

Caching personalized or sensitive data at the edge, which can leak the wrong content if the cache key is wrong.
Using `no-store` everywhere, which removes a big performance win and pushes avoidable load to the backend.
Assuming the CDN is magically fresh without setting proper cache rules and invalidation strategy.
Mixing static asset caching with user data caching, even though they need very different policies.
Creating too many cache variants through query strings, cookies, or headers, which fragments the cache and reduces hit rate.
Forgetting that stale content can be worse than slow content when users need a trustworthy answer.

Quiz

Quiz 1
What is the main trade-off in caching?
Think it through first, then reveal the answer.
Quiz 2
Which content is usually safest to cache aggressively?
Think it through first, then reveal the answer.
Quiz 3
What does `stale-while-revalidate` do?
Think it through first, then reveal the answer.
Quiz 4
Why should a cart or account state usually avoid edge caching?
Think it through first, then reveal the answer.
Quiz 5
Why is versioned asset naming so useful?
Think it through first, then reveal the answer.

Takeaways

Caching is a policy decision, not a binary on/off switch.
Different cache layers solve different problems, so know which layer is answering the request.
Use headers and versioning to make freshness intentional.
Private data needs more care than public assets, even when both live on the same page.