Next.js has several cache layers: request memoisation, the data cache, the full route cache and the client router cache. Most caching bugs come from not knowing which layer is active or from caching personalised data.
Key takeaways
- Target: Serve repeated requests from cache without showing stale or private data.
- Measure field data before choosing a fix.
- Change one bottleneck at a time and re-measure on a mid-range phone.
- A fast lab score is not a substitute for real-user data.
What Caching measures
Check response headers, build output, server logs and the behaviour of a second request. If a page is fast on the first load but stale after an update, the issue is usually revalidation rather than raw speed.
The fixes that usually move the number
- Use
revalidatefor content that can be stale for a known period. - Use
revalidateTagorrevalidatePathfor on-demand updates. - Mark authenticated or personalised routes as dynamic.
- Set
Cache-Controldeliberately at the CDN and origin. - Do not cache secrets or user-specific responses in a shared layer.
Symptom, cause and fix
| Symptom | Likely cause | What to do |
|---|---|---|
| Content is stale after an edit | No revalidation or a cached fetch | Add revalidatePath/revalidateTag and invalidate the CDN |
| User sees another user's data | Shared cache key or missing auth boundary | Make the route dynamic and scope the cache key |
| Slow every request | Everything is dynamic | Cache public data and stream personalised fragments |
| Different behaviour in dev | Dev mode bypasses some caching | Test against a production build |
How to measure it properly
- Map the layers. List which routes are static, dynamic, ISR or client-cached.
- Inspect headers. Check
Cache-Control,x-vercel-cacheor your CDN's cache status. - Test a second request. Compare latency and body before and after an update.
- Test invalidation. Edit content, trigger revalidation and confirm the change appears.
- Test auth. Log in as two users and verify no cross-user leakage.
A practical example
// Revalidate a tag after a content change
import { revalidateTag } from "next/cache";
export async function updateProduct(id: string, data: ProductInput) {
await db.product.update({ where: { id }, data });
revalidateTag("products");
}
Common mistakes
- Caching a page that includes a logged-in user's name or balance.
- Assuming
fetchcaching is the same as full-route caching. - Forgetting to invalidate the CDN after an origin revalidation.
- Using a very long
revalidatefor prices or stock.
A 30-day improvement plan
- Week 1. Collect field data and pick the worst route. Write down the baseline.
- Week 2. Fix the largest bottleneck and deploy behind a normal release.
- Week 3. Test on a mid-range phone and a throttled connection; fix regressions.
- Week 4. Compare field data, document the change and add a monitor or budget.
Field data vs lab data
| Source | What it tells you | How to use it |
|---|---|---|
| Field data | What real users experienced | The source of truth for the metric |
| Lab test | A reproducible diagnosis | Find the cause and test a fix |
| Synthetic monitoring | Availability and regressions over time | Alert on a budget breach |
| RUM segmentation | Device, country and route differences | Target the worst segment |
| Server timing | Database, cache and render time | Separate backend from frontend |
Set a performance budget
| Budget | Example starting point | Why |
|---|---|---|
| LCP | 2.5s or less at p75 | The main content must appear quickly |
| INP | 200ms or less at p75 | Interactions must feel responsive |
| CLS | 0.1 or less at p75 | Layout movement erodes trust |
| JavaScript | A route-level KB budget | Prevents bundle creep |
| Images | Responsive sizes and modern formats | The largest common payload |
| Third-party scripts | A named owner and a cap | Uncontrolled tags are a common regression |
Third-party script policy
- Every script needs an owner, a purpose and a review date.
- Load analytics and marketing tags after consent and after the main content.
- Prefer server-side events where they reduce client JavaScript.
- Test the checkout and sign-up with the full tag set enabled.
- Remove tags that no longer answer a business question.
Frequently asked questions
The target is Serve repeated requests from cache without showing stale or private data. A single fast test is not enough; use the 75th percentile of real page views and segment by mobile and desktop.
No. Start with the routes that matter to revenue or support: checkout, pricing, sign-up, the main product page and the most-read blog template. A fast homepage with a slow checkout is not a fast site.
It depends on traffic. Low-traffic sites can take weeks to accumulate enough data. Use lab tools for immediate feedback, but keep the field metric as the source of truth.
Often yes. Image dimensions, priority, bundle splits, caching and third-party scripts usually deliver more than a visual refresh. Redesign only after the measurement shows the layout itself is the bottleneck.
Sources and further reading
- Next.js caching and revalidation (nextjs.org)
- Next.js self-hosting guide (nextjs.org)
- Web Vitals (web.dev)
Related reading
Need a performance audit?
Send the URL and the metric that matters. I will identify the bottleneck, the likely fix and the cheapest way to prove it worked.
How to cite this guide: Nur Hossain Repon, “Caching Optimization Guide for Next.js and React Apps”, Nhrepon Portfolio, reviewed October 2026, https://nhrepon.com/posts/caching-optimization-guide-for-next-js-and-react-apps.





Comments
No comments yet.