Back to Guides

Caching Strategies & Best Practices

Implement effective caching strategies to dramatically reduce server load and improve page load times. Learn browser caching, CDN optimization, and advanced caching patterns.

Why Caching Matters for Performance

Caching stores copies of files or data in temporary storage locations, allowing faster retrieval on subsequent requests. Effective caching can reduce page load times by 50-80% for repeat visitors.

Benefits of Caching

  • Reduced Latency: Serve cached content faster than generating it on-demand
  • Lower Server Load: Fewer requests to origin server reduces CPU and database usage
  • Cost Savings: Reduced bandwidth and server costs from fewer origin requests
  • Better UX: Faster page loads improve user experience and engagement
  • Offline Support: Service workers enable offline functionality

Types of Caching

Browser Cache

Stores resources locally on user's device. Controlled by HTTP headers (Cache-Control, Expires).

CDN Cache

Content delivery networks cache static assets at edge locations worldwide for faster global delivery.

Server Cache

Server-side caching (Redis, Memcached) stores database queries, API responses, and rendered pages.

Browser Caching with HTTP Headers

Control browser caching behavior using Cache-Control and other HTTP headers. Proper configuration ensures browsers cache static assets while always fetching fresh dynamic content.

Cache-Control Header

The modern standard for caching. Directives control how and for how long resources are cached.

bash
# Nginx configuration for static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
# Cache for 1 year (immutable files with cache busting)
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML pages - revalidate on each request
location ~* \.html$ {
expires -1;
add_header Cache-Control "public, must-revalidate, max-age=0";
}
# API responses - no caching
location /api/ {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}

Common Cache-Control Directives

DirectiveDescriptionUse Case
max-age=31536000Cache for 1 yearVersioned static assets
publicCan be cached by any cacheStatic assets, public pages
privateBrowser cache onlyUser-specific data
no-cacheRevalidate before useDynamic content
no-storeNever cacheSensitive data, APIs
immutableNever revalidateCache-busted assets

Cache Busting Strategies

Use versioned filenames or query strings to force browsers to download new versions when files change.

html
<!-- Filename versioning (best) -->
<link rel="stylesheet" href="/styles.abc123.css" />
<script src="/app.def456.js"></script>
<!-- Query string versioning (fallback) -->
<link rel="stylesheet" href="/styles.css?v=1.2.3" />
<!-- Webpack/Next.js automatic hashing -->
<!-- Output: main.8f4c2a1b.js, styles.3d9e7f2c.css -->

CDN Caching Strategy

Content Delivery Networks (CDNs) cache static assets at edge locations worldwide, reducing latency for global users.

CDN Cache Configuration

bash
# Cloudflare Page Rules example
# Static assets - cache everything for 1 year
URL: yourdomain.com/static/*
Cache Level: Cache Everything
Edge Cache TTL: 1 year
Browser Cache TTL: 1 year
# HTML pages - cache for 1 hour
URL: yourdomain.com/*.html
Cache Level: Cache Everything
Edge Cache TTL: 1 hour
Browser Cache TTL: 1 hour
# API endpoints - bypass cache
URL: yourdomain.com/api/*
Cache Level: Bypass

CDN Best Practices

  • Use separate cache keys for mobile/desktop if serving different content
  • Enable HTTP/2 or HTTP/3 for faster multiplexed connections
  • Purge cache selectively when content changes (avoid full purges)
  • Set appropriate TTLs: Long for static assets (1 year), short for dynamic content (1 hour)
  • Use stale-while-revalidate to serve stale content while fetching fresh

Service Worker Caching (Advanced)

Service workers provide programmatic control over caching and enable offline functionality for Progressive Web Apps.

Basic Service Worker Cache

javascript
// service-worker.js
const CACHE_NAME = 'my-site-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
];
// Install event - cache critical assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});
// Fetch event - serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((response) => {
// Return cached version or fetch from network
return response || fetch(event.request);
})
);
});

Advanced Caching Strategies

javascript
// Strategy 1: Cache First (for static assets)
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then((cached) => cached || fetch(event.request))
);
});
// Strategy 2: Network First (for dynamic content)
self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request)
.catch(() => caches.match(event.request))
);
});
// Strategy 3: Stale While Revalidate
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((cached) => {
const fetchPromise = fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
return cached || fetchPromise;
});
})
);
});

Workbox (Recommended)

Google's Workbox library simplifies service worker development with battle-tested caching strategies.

javascript
// workbox-config.js
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
// Precache build artifacts
precacheAndRoute(self.__WB_MANIFEST);
// Cache images with expiration
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
}),
],
})
);
// API calls - network first with cache fallback
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new StaleWhileRevalidate({
cacheName: 'api-cache',
})
);

Server-Side Caching

Cache expensive database queries and API responses on the server to reduce processing time and database load.

Redis Caching Example

javascript
// Node.js with Redis
const redis = require('redis');
const client = redis.createClient();
async function getUser(userId) {
const cacheKey = `user:${userId}`;
// Try to get from cache first
const cached = await client.get(cacheKey);
if (cached) {
console.log('Cache hit');
return JSON.parse(cached);
}
// Cache miss - fetch from database
console.log('Cache miss');
const user = await db.users.findById(userId);
// Store in cache for 1 hour
await client.setEx(cacheKey, 3600, JSON.stringify(user));
return user;
}
// Invalidate cache on update
async function updateUser(userId, data) {
await db.users.update(userId, data);
await client.del(`user:${userId}`); // Clear cache
}

Next.js Server-Side Caching

javascript
// Next.js API route with caching
export async function GET(request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
// Cache for 1 hour with revalidation
const data = await fetch(`https://api.example.com/data/${id}`, {
next: { revalidate: 3600 }
});
return Response.json(await data.json());
}
// Static page with ISR (Incremental Static Regeneration)
export const revalidate = 3600; // Revalidate every hour
export default async function Page() {
const data = await fetchData();
return <div>{data}</div>;
}

Cache Invalidation Strategies

"There are only two hard things in Computer Science: cache invalidation and naming things." - Phil Karlton

Time-Based Expiration (TTL)

Set expiration times for cached content. Simple but may serve stale data.

bash
# Cache for specific duration
Cache-Control: max-age=3600, public # 1 hour
Cache-Control: max-age=86400, public # 1 day
Cache-Control: max-age=31536000, immutable # 1 year (versioned assets)

Event-Based Invalidation

Purge cache when content changes. More accurate but requires infrastructure.

javascript
// Purge CDN cache on content update
async function updateArticle(articleId, data) {
await db.articles.update(articleId, data);
// Purge CDN cache for this article
await cdn.purge(`/articles/${articleId}`);
await cdn.purge('/articles/list'); // Also purge list page
}

Stale-While-Revalidate

Serve cached content while fetching fresh data in background. Best UX.

bash
# Serve stale for up to 3600s while revalidating
Cache-Control: max-age=600, stale-while-revalidate=3600

Caching Implementation Checklist

Static assets cached for 1 year with immutable directive
Cache busting implemented (versioned filenames or query strings)
CDN configured with appropriate TTLs for different content types
HTML pages use short TTL or must-revalidate
Compression enabled (gzip/brotli) for cached assets
HTTP/2 or HTTP/3 enabled for CDN
Service worker implemented for offline support (PWA)
Server-side caching (Redis/Memcached) for database queries
Cache invalidation strategy in place for dynamic content
Monitoring cache hit rates and adjusting TTLs accordingly

Optimize Your Caching Strategy

Analyze your website's caching configuration and get AI-powered recommendations to improve cache efficiency and reduce load times.

Analyze Caching Performance