{"post":{"slug":"how-to-cache-api-responses-2026","title":"How to cache API responses in 2026: HTTP headers, Redis, and CDN strategies","excerpt":"Caching is the single fastest way to reduce API latency and database load. This is the 2026 guide to HTTP caching, in-process caches, Redis, and CDN layers.","category":"code","date":"March 29, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">C</span>aching is the cheapest performance win available to most API services — it reduces latency, cuts database queries, and absorbs traffic spikes that would otherwise collapse your origin. The mistake most teams make is either skipping it entirely or caching everything with the same TTL and then fighting stale data. The 2026 approach is layered and intentional.\n\n## What changed in 2026\n\n- **`stale-while-revalidate` became widely understood** and is now the default strategy for edge-cached API responses — serve the cached version immediately, refresh in the background.\n- **Valkey (the Redis fork) stabilised** as the open-source default after Redis relicensed; most managed services (Upstash, Elasticache) now offer both.\n- **CDN providers added API-aware caching.** Cloudflare Cache API and Fastly's Compute allow per-route cache logic at the edge without hitting your origin.\n- **ORM-level caching emerged.** Drizzle and Prisma both offer query-result caching integrations, making the application layer easier to cache without explicit Redis calls.\n\n## The caching layers\n\n| Layer | Where it lives | Best for |\n| --- | --- | --- |\n| In-process (Map / LRU) | App memory | Single-instance, read-heavy, small datasets |\n| Shared cache (Redis/Valkey) | Separate service | Multi-instance, session data, computed results |\n| HTTP cache (CDN/proxy) | Edge / browser | Public API responses, static data |\n| Database query cache | DB or ORM layer | Expensive query results |\n\nUse the lowest layer that meets your needs. In-process is fastest; HTTP cache scales for free.\n\n## HTTP cache headers (free wins)\n\n```http\n# Public response, cache for 60 s, serve stale up to 10 s while refreshing\nCache-Control: public, max-age=60, stale-while-revalidate=10\n\n# Private (per-user) response, no proxy caching\nCache-Control: private, max-age=300\n\n# Never cache (mutations, auth tokens)\nCache-Control: no-store\n```\n\nSet `ETag` or `Last-Modified` on responses so clients can make conditional requests that return `304 Not Modified` when the content has not changed — reducing body transfer costs.\n\n## Redis caching patterns\n\n**Cache-aside (lazy loading)** is the most common pattern:\n\n```typescript\nasync function getProduct(id: string): Promise<Product> {\n  const cached = await redis.get(`product:${id}`);\n  if (cached) return JSON.parse(cached);\n\n  const product = await db.products.findUnique({ where: { id } });\n  if (!product) throw new NotFoundError();\n\n  await redis.setex(`product:${id}`, 300, JSON.stringify(product)); // TTL 5 min\n  return product;\n}\n```\n\n**Write-through** keeps cache and DB in sync on every write — higher write cost, but no cold cache on read:\n\n```typescript\nasync function updateProduct(id: string, data: Partial<Product>) {\n  const updated = await db.products.update({ where: { id }, data });\n  await redis.setex(`product:${id}`, 300, JSON.stringify(updated));\n  return updated;\n}\n```\n\n## Cache invalidation\n\nTTL-based invalidation is simple but causes stale windows. For tighter consistency, use event-driven invalidation:\n\n```typescript\n// On product update event:\nawait redis.del(`product:${productId}`);\nawait redis.del(`product-list:category:${product.categoryId}`);\n```\n\nFor complex relationships, use **versioned keys**: `product:${id}:v${version}`. Increment the version on write; old keys expire naturally.\n\n## How to pick a caching strategy\n\n1. **Public, user-agnostic data?** HTTP `Cache-Control` + CDN. Zero extra infrastructure.\n2. **Computed results shared across users?** Redis cache-aside with a TTL matching your staleness tolerance.\n3. **Per-user personalised data?** `Cache-Control: private` at HTTP; Redis with a user-scoped key.\n4. **Expensive aggregations?** Pre-compute on a schedule and cache the result — background job writes, API reads.\n5. **Real-time data that must never be stale?** Do not cache; use a WebSocket or server-sent events.\n\n## Common mistakes\n\n**Caching with one TTL for everything.** Product listings and user profile pictures have wildly different staleness tolerance. Set TTLs per resource type.\n\n**Forgetting cache on write paths.** Cache-aside only fills on reads; a surge of reads after a cold deploy hammers the DB. Pre-warm critical keys on startup.\n\n**Not caching errors.** A flapping downstream service will miss-cache on every request. Cache short-TTL error states (5–15 s) to absorb the load.\n\n**Using request bodies as cache keys.** POST responses almost never should be cached, and POST bodies are not reliably comparable as keys.\n\n**No cache hit rate monitoring.** A cache with a 30% hit rate might as well not exist. Track hit rate and miss latency; alert when hit rate drops.\n\n## What to skip\n\n- **Caching mutations** (POST, PUT, DELETE) — never. Return fresh data or redirect.\n- **In-process caches for multi-instance services** without a shared invalidation mechanism — different pods serve different data.\n- **Infinite TTLs** without an invalidation path — your cache will drift from reality with no way back.\n\n## FAQ\n\n**What TTL should I use?**\nMatch it to how often the underlying data actually changes. Product prices: minutes. Static reference data: hours or days. User-generated content: seconds to minutes.\n\n**How do I avoid cache stampedes?**\nUse a probabilistic early expiration (PER) or a mutex lock on cache miss so only one request rebuilds the cache while others wait briefly.\n\n**Is Redis still the right choice in 2026?**\nYes for most teams. Valkey (the open-source Redis fork) is a drop-in replacement. Upstash offers serverless Redis with per-request billing that works well for lower-traffic APIs.\n\n**How do I cache in a serverless function?**\nIn-process caching does not survive cold starts. Use an external store (Redis/Upstash) or rely on HTTP cache headers with a CDN sitting in front.\n\n## Where to go next\n\nSee [How to paginate an API in 2026](/blog/how-to-paginate-an-api-2026), [How to monitor a service in 2026](/blog/how-to-monitor-a-service-2026), and [How to optimize SQL queries in 2026](/blog/how-to-optimize-sql-queries-2026).","hue":315,"metaTitle":"How to Cache API Responses in 2026: HTTP, Redis, CDN","metaDescription":"Cache API responses correctly in 2026 — HTTP headers, Redis patterns, CDN strategies, TTL choices, and invalidation without stale data bugs.","keywords":["how to cache api responses 2026","api caching best practices","redis cache api 2026","http cache headers","cache-control max-age","cdn api caching","cache invalidation strategies","in-process cache vs redis","stale-while-revalidate 2026","api performance optimization"],"tldr":["**Use HTTP cache headers first** — they are free and handled by proxies and browsers without extra infrastructure.","**Layer caches by TTL** — short for personalised data, long for public/static, zero for mutations.","**Redis is the default shared cache** for multi-instance services; in-process cache is fine for single-instance or immutable data.","**Cache invalidation is the hard part** — use versioned keys or event-driven purge rather than guessing TTLs.","**Skip:** caching POST/PUT/DELETE responses — mutations must not be cached; only idempotent reads belong in a cache."],"readTime":"4 min read","wordCount":878},"related":[{"slug":"caching-strategies-2026","title":"Caching strategies in 2026: which pattern to use and when","excerpt":"Cache-aside, write-through, read-through, and CDN edge caching solve different problems. Here is the 2026 decision guide with concrete tradeoffs.","category":"code","date":"April 13, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":322},{"slug":"how-to-cache-with-redis-2026","title":"How to cache with Redis in 2026: patterns, TTLs, and invalidation","excerpt":"Redis caching in 2026 is faster to set up than ever, but the same mistakes persist: missing TTLs, stale data, and cache stampedes. This guide fixes all three.","category":"code","date":"March 11, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":307},{"slug":"redis-vs-memcached-2026","title":"Redis vs Memcached in 2026: which cache should you use?","excerpt":"Redis and Memcached are both fast in-memory caches, but Redis has grown into a multi-purpose data store. Here is when each makes sense in 2026.","category":"code","date":"April 21, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":315}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"how-to-fix-a-leaky-faucet-2026","title":"How to fix a leaky faucet in 2026: step-by-step for every faucet type","excerpt":"A dripping faucet wastes thousands of gallons per year and the repair is almost always under $20. Here is how to diagnose and fix every common faucet type in 2026.","category":"guides","date":"March 30, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":270},"older":{"slug":"how-to-calculate-take-home-pay-2026","title":"How to calculate take-home pay in 2026: what actually comes out of your check","excerpt":"Your gross salary and your take-home pay can differ by 25–40%. Here is exactly what gets deducted and how to calculate your real net pay in 2026.","category":"finance","date":"March 29, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":300}}