Skip to content

Caching Strategies — Browser, CDN, and Server-Side Explained

DodaTech Updated 2026-06-23 7 min read

In this tutorial, you will learn how to implement caching at three levels, browser, CDN edge, and server-side, to dramatically reduce page load times and backend load. Caching is the single most impactful performance optimization because it avoids redundant network requests and computation. DodaZIP uses aggressive caching for its download assets, serving over 80 percent of requests directly from edge caches.

What You Will Learn

  • How to configure browser caching with Cache-Control and ETag headers
  • How to set up a CDN cache layer with purge strategies
  • How to implement server-side caching with Redis and Memcached
  • How to avoid common caching pitfalls like stale content and cache poisoning

Why It Matters

Every millisecond of latency costs conversions. A well-implemented caching strategy can reduce Time to First Byte (TTFB) by 60 to 80 percent and cut origin server load by an order of magnitude. DodaTech serves millions of requests daily; without caching the infrastructure cost would be prohibitive.

Real-World Use Case

The Doda Browser extension update mechanism uses a three-tier cache: the service worker cache on the client, a Cloudflare CDN cache at the edge, and a Redis cache on the origin server. Update checks complete in under 50 milliseconds for 90 percent of users.

Prerequisites

You should understand HTTP request-response semantics, how DNS resolution works, and basic NGINX or Apache configuration. Familiarity with Redis is helpful for the server-side section.

Step-by-Step Tutorial

Step 1: Configure Browser Caching Headers

Browser caching stores static assets locally so repeat visits load instantly. Use Cache-Control headers to define caching policies.

# NGINX configuration for browser caching
location /static/ {
    expires 365d;
    add_header Cache-Control "public, immutable";
}

location /images/ {
    expires 30d;
    add_header Cache-Control "public";
}

Expected behavior: After the first visit, static assets are loaded from the local disk cache instead of the network. The Network tab in DevTools shows "(disk cache)" for these requests.

Step 2: Implement ETags for Validation

ETags provide cache validation without downloading the entire file. The browser sends a conditional request with the ETag value, and the server responds with 304 Not Modified if the content has not changed.

// Express.js ETag setup
const express = require('express');
const app = express();
app.set('etag', 'strong'); // Use strong ETag (hash-based)
app.use('/static', express.static('public', {
  etag: true,
  lastModified: true,
  maxAge: '30d'
}));

Expected output: Response headers include ETag: "abc123". Subsequent requests include If-None-Match: "abc123" and receive a 304 response with zero bytes.

Step 3: Set Up CDN Caching

CDN caching stores content at edge locations close to users. This section uses Cloudflare as an example.

# Using Cloudflare API to set page rule cache settings
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/pagerules" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "targets": [{"target": "url", "constraint": {"operator": "matches", "value": "*dodatech.com/static/*"}}],
    "actions": [{"id": "cache_level", "value": "cache_everything"}]
  }'

Expected result: Static assets are cached at Cloudflare edge nodes. The cf-cache-status: HIT header confirms the asset was served from the edge.

Step 4: Implement Server-Side Caching with Redis

Server-side caching stores computed results to avoid redundant database queries or API calls.

// Node.js Redis caching example
const redis = require('redis');
const client = redis.createClient();
const express = require('express');
const app = express();

async function getUserData(userId) {
  const cacheKey = `user:${userId}`;
  const cached = await client.get(cacheKey);
  if (cached) return JSON.parse(cached);
  
  // Simulate expensive database query
  const data = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
  await client.setEx(cacheKey, 3600, JSON.stringify(data));
  return data;
}

app.get('/api/user/:id', async (req, res) => {
  const data = await getUserData(req.params.id);
  res.json(data);
});

Expected behavior: First request hits the database and takes 200ms. Subsequent requests within the TTL hit Redis and take under 5ms.

Step 5: Create a Cache Invalidation Strategy

Caching is useless without a proper invalidation strategy. Common approaches include:

  • Time-based expiration (TTL)
  • Event-based purging (purge cache when content updates)
  • Versioned URLs (cache-busting with hash in filename)
# Purge Cloudflare cache programmatically
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"files": ["https://dodatech.com/static/main.css"]}'

Step 6: Implement Service Worker Caching

For progressive web apps, a service worker provides an additional caching layer that works offline.

// Service worker install event - precache static assets
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('v1').then(cache => {
      return cache.addAll([
        '/',
        '/static/main.css',
        '/static/app.js',
        '/static/logo.webp'
      ]);
    })
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cached => {
      return cached || fetch(event.request);
    })
  );
});

Expected behavior: After the service worker installs, the app loads from cache even when offline.

Learning Path

flowchart LR
  A[Core Web Vitals] --> B[Caching Strategies]
  B --> C[CDN Configuration]
  B --> D[Reduce TTFB]
  C --> E[HTTP/2 and HTTP/3]
  D --> E
  
  style B fill:#4f46e5,color:#fff
  style A fill:#6366f1,color:#fff
  style C fill:#6366f1,color:#fff

Common Errors

  1. Setting a short TTL on all assets: Static assets like fonts and logos rarely change. Use a one-year TTL with cache-busting hashed filenames.

  2. Caching authenticated content without care: Never cache personalized content with public Cache-Control. Use private, no-cache for authenticated responses.

  3. Ignoring cache headers from the origin: Your CDN respects origin cache headers by default. If the origin sends Cache-Control: no-cache, the CDN will not cache.

  4. Forgetting to purge cached data after updates: Deploying new CSS or JavaScript without updating filenames or purging the cache leads to stale assets for users.

  5. Over-caching API responses: Aggressively caching API responses can serve stale data. Use short TTLs with stale-while-revalidate for frequently changing data.

  6. No cache key variation for mobile vs desktop: CDNs cache by URL by default. Serve different content to mobile and desktop users without varying the cache key, causing wrong content delivery.

Practice Questions

  1. What is the difference between Cache-Control: public and private?
  2. How does an ETag improve cache efficiency?
  3. What is cache busting and why is it needed?
  4. Describe a three-tier caching architecture.
  5. How does stale-while-revalidate work?

Answers: 1. public allows any cache to store the response; private restricts caching to the browser only. 2. ETags allow the server to return 304 Not Modified without re-downloading the body. 3. Cache busting uses unique filenames (via hash) to force browsers to fetch new versions when content changes. 4. Browser cache, CDN edge cache, and origin server cache (Redis). 5. It serves stale content immediately while fetching fresh content in the background, then updates the cache for the next request.

Challenge

Build a caching proxy using Node.js that sits between the client and a slow API. Cache responses in Redis with a 60-second TTL, implement ETag-based conditional requests, and set Cache-Control headers appropriate for public API responses.

FAQ

What is the difference between browser cache and CDN cache?

Browser cache stores files on the user device for repeat visits. CDN cache stores files on edge servers distributed globally to reduce latency for users far from the origin.

How long should I set cache expiration for static assets?

Set a long expiration, 365 days, for versioned assets (filenames containing a hash). For non-versioned assets, 30 days is a reasonable default.

Can caching cause security issues?

Yes, caching sensitive data like user profiles or API tokens can expose private information. Never cache responses with Authorization headers unless using private Cache-Control.

What is the best cache invalidation strategy?

Surrogate keys or tag-based purging combined with short TTLs is the most flexible approach. Purge by tag when content changes, and fall back to TTL expiration for cleanup.

Does DodaZIP use caching for file downloads?

DodaZIP uses a multi-tier cache with Cloudflare CDN and browser caching for its installer files. The cache TTL is 7 days for installer versions and 365 days for static documentation assets.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro