Effektives Caching kann Response-Zeiten von 3 Sekunden auf 50ms reduzieren - ein 60x Speedup! In diesem Guide zeigen wir, wie Sie Browser-Cache, CDN, Redis und Service Workers richtig einsetzen.
Die Caching-Hierarchie
User Request
↓
1. Browser Cache (0-10ms)
↓ (miss)
2. Service Worker (10-50ms)
↓ (miss)
3. CDN Cache (50-200ms)
↓ (miss)
4. Server Memory (Redis) (5-20ms)
↓ (miss)
5. Database (100-500ms)
Ziel: Requests so weit oben wie möglich beantworten.
Browser Caching
HTTP Cache Headers
// Next.js API Route
export async function GET() {
const data = await getStaticData()
return NextResponse.json(data, {
headers: {
// Cache 1 Stunde, revalidate im Background
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
},
})
}
Cache-Control Direktiven:
public
- Auch Proxies dürfen cachenprivate
- Nur Browser cachtno-cache
- Revalidate vor Nutzungno-store
- Nie cachenmax-age=3600
- Browser cached 1hs-maxage=3600
- CDN cached 1hstale-while-revalidate=86400
- Background revalidation
Cache-Strategie nach Content-Typ
// Statische Assets
'Cache-Control': 'public, immutable, max-age=31536000'
// API Responses (häufig ändernd)
'Cache-Control': 'public, max-age=60, stale-while-revalidate=300'
// User-spezifische Daten
'Cache-Control': 'private, no-cache'
// Nie cachen
'Cache-Control': 'no-store'
Next.js App Router Caching
Fetch Caching
// Default: Cache unbegrenzt
const data = await fetch('https://api.example.com/data')
// Cache 1 Stunde
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 }
})
// Nie cachen
const user = await fetch('https://api.example.com/user', {
cache: 'no-store'
})
// Force cache (auch wenn expired)
const static = await fetch('https://api.example.com/config', {
cache: 'force-cache'
})
Route Segment Config
// app/blog/page.tsx
export const revalidate = 3600 // Revalidate every hour
export const dynamic = 'force-static' // Force static at build
export const fetchCache = 'default-cache'
export default async function BlogPage() {
const posts = await getPosts()
return <PostList posts={posts} />
}
Programmatic Cache Revalidation
// app/actions/revalidate.ts
'use server'
import { revalidatePath, revalidateTag } from 'next/cache'
export async function updatePost(id: string) {
await db.post.update({ where: { id }, data: { ... } })
// Revalidate specific path
revalidatePath(`/blog/${id}`)
// Or revalidate by tag
revalidateTag('posts')
}
// Fetch mit Tag
const posts = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
})
CDN Caching
Edge Network Configuration
// next.config.js
module.exports = {
headers: async () => [
{
source: '/api/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, s-maxage=60, stale-while-revalidate=300',
},
],
},
],
}
Cloudflare Cache Rules
// Custom Cache-Control per Route
export async function GET(request: Request) {
const data = await getData()
return new Response(JSON.stringify(data), {
headers: {
'Cache-Control': 'public, max-age=3600',
'CDN-Cache-Control': 'max-age=86400', // Cloudflare-specific
},
})
}
Cache Purging
// Purge Cloudflare Cache
async function purgeCache(urls: string[]) {
await fetch('https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CLOUDFLARE_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ files: urls }),
})
}
Redis Caching
Setup Redis Client
// lib/redis.ts
import { Redis } from '@upstash/redis'
export const redis = new Redis({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
})
Cache Wrapper Pattern
// lib/cache.ts
import { redis } from './redis'
export async function withCache<T>(
key: string,
fetcher: () => Promise<T>,
ttl: number = 3600
): Promise<T> {
// Try to get from cache
const cached = await redis.get<T>(key)
if (cached) return cached
// Fetch fresh data
const fresh = await fetcher()
// Store in cache
await redis.setex(key, ttl, JSON.stringify(fresh))
return fresh
}
Usage in API Routes
// app/api/posts/route.ts
import { withCache } from '@/lib/cache'
export async function GET() {
const posts = await withCache(
'posts:all',
async () => {
return await db.post.findMany()
},
3600 // 1 hour
)
return NextResponse.json(posts)
}
Cache Patterns
1. Cache-Aside (Lazy Loading)
async function getUser(id: string) {
const cacheKey = `user:${id}`
// Check cache
let user = await redis.get(cacheKey)
if (!user) {
// Load from DB
user = await db.user.findUnique({ where: { id } })
// Store in cache
await redis.setex(cacheKey, 3600, JSON.stringify(user))
}
return user
}
2. Write-Through
async function updateUser(id: string, data: any) {
// Update DB
const user = await db.user.update({
where: { id },
data,
})
// Update cache immediately
await redis.setex(`user:${id}`, 3600, JSON.stringify(user))
return user
}
3. Write-Behind (Async)
async function trackPageView(pageId: string) {
// Increment counter in Redis immediately
await redis.incr(`pageviews:${pageId}`)
// Background job writes to DB every 5 minutes
// (not shown here)
}
Service Worker Caching
Basic Service Worker
// public/sw.js
const CACHE_NAME = 'v1'
// Cache static assets on install
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
'/',
'/offline.html',
'/images/logo.png',
])
})
)
})
// Serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request)
})
)
})
Next.js PWA Integration
pnpm add next-pwa
// next.config.js
const withPWA = require('next-pwa')({
dest: 'public',
register: true,
skipWaiting: true,
})
module.exports = withPWA({
// Your Next.js config
})
Advanced Caching Strategies
// Workbox Strategies
import { registerRoute } from 'workbox-routing'
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies'
// Images: Cache First
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({ cacheName: 'images' })
)
// API: Network First
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({ cacheName: 'api' })
)
// CSS/JS: Stale While Revalidate
registerRoute(
({ request }) => request.destination === 'style' || request.destination === 'script',
new StaleWhileRevalidate({ cacheName: 'static-resources' })
)
Caching Best Practices
1. Cache Invalidation Strategie
// Tag-based Invalidation
const CACHE_TAGS = {
POSTS: 'posts',
USER: (id: string) => `user:${id}`,
PRODUCTS: 'products',
}
// Revalidate all posts
await revalidateTag(CACHE_TAGS.POSTS)
// Revalidate specific user
await revalidateTag(CACHE_TAGS.USER('123'))
2. Cache Warming
// Warm cache on deploy
async function warmCache() {
const criticalPaths = [
'/api/products',
'/api/categories',
'/api/featured',
]
await Promise.all(
criticalPaths.map(path =>
fetch(`https://example.com${path}`)
)
)
}
3. Stale Content Handling
// Stale-While-Revalidate Pattern
async function getData() {
const cacheKey = 'data:key'
// Return stale data immediately
const stale = await redis.get(cacheKey)
// Revalidate in background
revalidateInBackground(cacheKey)
return stale
}
async function revalidateInBackground(key: string) {
const fresh = await fetchFreshData()
await redis.setex(key, 3600, JSON.stringify(fresh))
}
Performance Monitoring
// Track Cache Hit Rates
export async function GET(req: Request) {
const cacheKey = 'data:key'
const start = Date.now()
const cached = await redis.get(cacheKey)
const isCacheHit = !!cached
// Log metrics
await logMetric({
route: req.url,
cacheHit: isCacheHit,
duration: Date.now() - start,
})
return NextResponse.json(cached || await fetchData())
}
Real-World Results
Bei unserem E-Commerce-Projekt:
Vor Caching:
- API Response Time: 420ms
- Page Load: 2.8s
- Server Requests/min: 12,000
Nach Multi-Layer Caching:
- API Response Time: 45ms (-89%)
- Page Load: 0.9s (-68%)
- Server Requests/min: 2,400 (-80%)
- Cache Hit Rate: 94%
Cost Savings: -75% Server-Kosten
Zusammenfassung
Effektives Caching nutzt mehrere Layer:
✅ Browser Cache für statische Assets ✅ CDN für geografisch verteilte Inhalte ✅ Redis für dynamische Daten ✅ Service Workers für Offline-Support ✅ Next.js Caching für optimale Integration
Resultat: 60-90% schnellere Response-Zeiten.
Caching-Architektur Beratung?
Als Performance-Spezialisten implementieren wir:
- 🏗️ Multi-Layer Caching-Architektur
- ⚡ Redis/CDN Setup und Optimierung
- 📊 Cache Performance Monitoring
- 🔄 Invalidation-Strategien
Aktualisiert: Mai 2024