This episode covers caching strategies with Cache-Control and ISR, edge caching and CDN integration, optimizing data fetching to reduce API latency, and prefetching with resource scheduling to speed up the application.

The network is the slowest part of a web application. Requests must pass through DNS, TLS, and many server hops before data arrives. The answer isn't upgrading the server, but avoiding repeated work through caching.
Episode 14 covers caching strategies with Cache-Control and ISR, edge caching and CDN integration, optimizing data fetching to reduce API latency, and prefetching with resource scheduling.
The Cache-Control header tells the browser and CDN how long a response may be stored. Set it in a route handler:
export async function GET() {
const data = await ambilData()
return Response.json(data, {
headers: {
"Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
},
})
}The header above indicates the response may be cached publicly for 60 seconds, and when the cache goes stale, the old content keeps being served while it's refreshed in the background for 300 seconds. This is the stale-while-revalidate pattern used for data that doesn't need to always be fresh.
This pattern is also known as SWR caching and is widely used on news sites: readers always see content without waiting, while data is updated in the background. Set s-maxage according to your business's data freshness tolerance — the more often the data changes, the smaller the safe value.
ISR and Cache-Control work together: ISR determines when static pages are rebuilt on the server, while the header controls how long the result may be cached at the edge and in the browser. For static pages, use next: { revalidate: 60 } on fetch; for route handlers, use a header like the example above. Both produce fast responses with reasonably fresh data.
A CDN (Content Delivery Network) stores copies of content on many servers around the world. When a user in Jakarta accesses an application deployed on a server in America, the CDN serves from the nearest server — dramatically cutting latency.
The browser cache stores responses for a single user; the CDN cache stores them for many users at once and has a much bigger impact on origin load. Both are controlled by the same headers, but with values tuned to each need.
Static pages and assets in the public folder are cached by the CDN automatically on platforms like Vercel. To make full use of a CDN:
Not all content may be cached. Pages that display personal data must use Cache-Control: private or no-store. This separation matters: a single cache misconfiguration can leak one user's data to another. Always validate headers in production before releasing a new page.
When data changes faster than the TTL, you need a way to invalidate the cache explicitly. Platforms like Vercel provide a purge cache API, or use revalidatePath from episode 6 to trigger a page update before the TTL expires.
API latency comes from network time and server processing. Effective strategies: combine several pieces of data into one endpoint, avoid request waterfalls (waiting for request A before sending request B), and parallelize independent requests:
const [userRes, postsRes] = await Promise.all([
fetch("/api/user"),
fetch("/api/posts"),
])The Promise.all above runs two fetches simultaneously, cutting total wait time to the slowest request rather than the sum of both. Next.js also caches the result of identical fetches during a single request.
A common mistake is caching dynamic pages that hold personal data. Always separate public and private content: private pages use no-store, while public pages make full use of the CDN. A misconfiguration in this area could leak user data to other users.
For public APIs used by other applications, document your caching policy with explicit headers and provide versioned endpoints when the contract changes.
Cache rarely changing data on the client side with TanStack Query — the same query won't be refetched when another component uses it. Combining server and client caching significantly reduces the number of API calls.
Also watch response size: fetching a hundred fields when only three are used wastes bandwidth and slows parsing. Select fields with the API's selectors, or build a concise dedicated endpoint.
Next.js already handles basic prefetching: the Link component loads the destination page before it's clicked. For data, use the prefetch option on TanStack Query for queries likely to be needed — for example when the user hovers over a product detail button. Other important resource scheduling: give LCP images priority, defer non-critical scripts, and avoid blocking render with large JavaScript.
Every caching strategy change should be measured: compare TTFB and LCP before and after, and watch the cache hit rate in the platform dashboard. Good caching improves hit rate without sacrificing data freshness. revalidate: 60 is an example of an option whose impact on data freshness versus response speed can be measured.
Here's what to take away:
In the next episode, episode 15, we'll discuss performance optimization — profiling with React DevTools and browser tools, code splitting and bundle analysis with tree shaking, Core Web Vitals optimization, and reducing JavaScript payload with server components and dynamic import. Your application will be measured and optimized methodically.