Web App Caching Strategy: CDN, Redis and Browser Layers Explained

Web App Caching Strategy: CDN, Redis and Browser Layers Explained — cover image

Most slow web apps are not slow because the code is bad. They are slow because the same expensive work is done again and again. The same product list is queried from the database a thousand times an hour. The same image is fetched from a distant server by every visitor. The same report is recalculated each time someone opens a dashboard.

Caching solves this by remembering results so the work happens once. Done well, it makes pages faster, lowers infrastructure cost and helps your app survive traffic spikes. Done badly, it shows users stale prices, leaks private data or hides bugs. This guide explains a layered caching strategy for web apps, with practical rules for what to cache, where, and for how long.

The four layers of caching

A request can be answered at several points between the user and your database. The closer to the user, the faster and cheaper the response.

1. Browser cache

The user's own browser stores files such as scripts, stylesheets and images. A returning visitor loads them from disk with no network request at all. You control this with HTTP headers like Cache-Control and ETag.

2. CDN cache

A content delivery network keeps copies of your files on servers around the world. A visitor in Mumbai gets assets from a nearby location rather than from a single origin server in another continent. CDNs can also cache full HTML pages and some API responses.

3. Application cache

Inside your infrastructure, an in-memory store such as Redis holds computed results: query outputs, session data, rendered fragments and rate limit counters. This layer protects your database from repeated work.

4. Database cache

Databases keep frequently used data pages in memory and may cache query plans. You mostly benefit from this by good indexing and sufficient memory rather than by managing it directly.

A real-world example: a catalogue-heavy online store

Here is an illustrative scenario. An online store has a few thousand products, and its category pages run several database queries each, including filters, prices and stock counts. During a festival sale, traffic surges and the database struggles. Pages slow down exactly when customers are most eager to buy.

The team adds layers. Static assets get long-lived browser caching with fingerprinted filenames. Images and category pages go through a CDN with a short expiry. Product lists and filter counts are cached in Redis for a minute or two. Stock counts and cart contents remain uncached or very short-lived, since they must be accurate. The database now sees only a fraction of the earlier load, and the pages feel faster. A change in price triggers an explicit purge of the affected product and category pages so customers never see outdated amounts.

The lesson is that not everything deserves the same treatment. Static and semi-static data are cached aggressively; transactional data is cached carefully or not at all.

Step-by-step: designing a caching strategy

  1. Measure first. Use profiling and tracing to find the slowest routes and the most repeated queries. Cache what the data shows, not what you guess.
  2. Classify your data. Sort content into static (never changes), semi-static (changes occasionally), personalised (differs per user) and real-time (must be exact).
  3. Set browser rules for assets. Fingerprint filenames (for example app.3f9a1c.js) and use long expiry such as a year with the immutable directive, so a new deploy simply uses a new filename.
  4. Put a CDN in front. Cache images, fonts and downloads. For HTML and public API responses, set explicit s-maxage values and use stale-while-revalidate so users get quick responses while the cache refreshes in the background.
  5. Add Redis for expensive reads. Cache the output of slow queries and computations, keyed carefully (for example products:category:42:page:1), with a time to live that matches how fresh the data must be.
  6. Choose a pattern. Cache-aside (check the cache, fall back to the database, then store) is the most common and easiest to reason about. Write-through and write-behind exist, but add complexity.
  7. Define invalidation per data type. Decide whether each entry expires on a timer, is purged on change, or both. Document who owns each rule.
  8. Prevent cache stampedes. When a popular key expires, many requests can hit the database at once. Use request coalescing, locks, jittered expiry or serve stale data while one worker refreshes.
  9. Protect private data. Mark personalised responses private or no-store, and use Vary headers correctly so a CDN never serves one user's page to another.
  10. Monitor and tune. Track hit ratio, eviction rate, memory use and latency, and review them after each major release. Tracing tools help here, as covered in our guide to performance budgets for web apps.

Cache invalidation without the headaches

The famous joke says there are only two hard problems in computing, and cache invalidation is one of them. These approaches make it manageable:

Whichever you choose, write down the maximum acceptable staleness for each data type. That single sentence prevents most arguments later.

Common mistakes

Redis tips for production

Key benefits of a layered strategy

HTTP cache headers cheat sheet

Many caching bugs come from misunderstood headers. These are the ones you will use most often:

Check your configuration with browser developer tools and a command-line request, rather than trusting framework defaults. Many frameworks add conservative headers that quietly disable caching on pages that could safely be cached.

Caching in modern frameworks

Frameworks such as Next.js, Nuxt and Astro offer built-in options like static generation, incremental regeneration and edge caching. They are powerful, but they can hide complexity. Know which pages are generated at build time, which are regenerated on a timer, and which are rendered per request. Document these decisions, because a developer who changes a data fetch can unintentionally turn a cached page into a dynamic one, or the reverse, and the effect only appears under load.

Measuring the impact

Before changing anything, record median and slow-tail response times, database queries per request and origin server load. After each change, repeat the measurements under similar conditions. For example, you might discover that browser and CDN caching improve the loading of static assets considerably, while Redis mainly reduces database pressure rather than visible page speed. This evidence helps you decide where the next hour of effort should go, and it gives non-technical stakeholders a clear picture of why caching work is worth the time.

When not to cache

Skip caching for data that must be exact at every moment, such as account balances, payment status, inventory at the final checkout step and security decisions. Also skip it when the underlying query is simply slow because of a missing index, since fixing the cause is better than hiding it. Caching is a tool for repeated, read-heavy work, not a cure for every performance complaint.

Conclusion

Caching is one of the highest-return performance improvements available, but it rewards discipline. Measure first, classify your data, cache aggressively where content is static, cache carefully where it is personal or transactional, and write down your invalidation rules. Start with browser and CDN caching, add Redis for the expensive reads, and monitor hit rates as you go. If you are planning a high-traffic product or struggling with slow pages, our web development team can audit your architecture and design a caching layer that fits your traffic and your budget.

Frequently Asked Questions

What should I cache first?
Start with static assets on a CDN, then cache expensive, read-heavy data such as product listings or computed reports in Redis. Measure before and after so you cache what actually matters.
Why is cache invalidation considered hard?
Because stale data can mislead users, and knowing exactly when to remove or refresh cached values across several layers requires clear rules about ownership and expiry.
Is Redis a database or a cache?
Redis is an in-memory data store commonly used as a cache. It can persist data, but most teams treat it as disposable and keep the source of truth in a primary database.
Can caching cause security problems?
Yes. Caching personalised or private responses on a shared CDN can leak one user's data to another. Use correct cache headers and vary rules, and never cache authenticated responses publicly.
How do I know my cache is working?
Track hit ratio, latency and origin load. A healthy cache shows a high hit rate on cacheable routes and a visible drop in database queries.