The Hidden Pitfall of Next.js Fetch Revalidation: A Real-World Debugging Story

The Hidden Pitfall of Next.js Fetch Revalidation: A Real-World Debugging Story

July 13, 2025 · 3 min read

TL;DR

Setting Next.js fetch revalidation to very large values like 31536000 (1 year) or Infinity on Vercel causes the server to serve a broken cached response with wrong headers and no data after the first deploy. The fix is to cap revalidate at a reasonable value, typically 3600 seconds or less.

Next.js is used by over 800,000 websites worldwide as of 2024.

BuiltWith

Vercel's edge network serves over 100 billion requests per month.

Vercel

When building FounderSignal, a platform for startup idea validation, I ran into a subtle but critical issue with Next.js’s fetch revalidation that every developer should know about. This post will walk you through the problem, the debugging journey, and the solution, with practical code snippets and lessons learned.


The Problem: Stale Data, Strange Headers

I wanted to cache API responses for a long time, ideally, a year, using Next.js’s fetch revalidation. My fetch looked like this:

const response = await fetch("https://api.jsonplaceholder.com/v1/users", {
  next: { revalidate: 31536000 }, // 1 year in seconds
});

It worked locally and on the first deploy.
But after refreshing the page on Vercel, I started seeing this:

  • content-type: text/xml
  • content-length: 0
  • No actual data, even though my API was returning the correct JSON.

The Debugging Journey

At first, I suspected my API. I checked logs, tested endpoints, and confirmed the API was returning the right data and headers.

But the deployed Next.js app was serving an empty response with the wrong content type. Why?

The Culprit: Extremely Long or Infinite Revalidation

After hours of debugging, I discovered the issue:
Setting revalidate: 31536000 (1 year) or Infinity caused Vercel’s Next.js server to serve a broken cached response after the initial fetch.

Even though Next.js docs mention Infinity is supported, in practice, it led to:

  • Stale or empty cache entries
  • Wrong content-type headers
  • No data on subsequent requests

The Fix: Use a Reasonable Revalidation Interval

Switching to a 1-day revalidation fixed everything:

const response = await fetch("https://api.jsonplaceholder.com/v1/users", {
  next: { revalidate: 86400 }, // 1 day in seconds
});

Now, every page refresh returns the correct data, and the cache works as expected.


Key Takeaways for Next.js Developers

1. Don’t Use Extremely Long or Infinite Revalidation

Even if the docs say Infinity is supported, it may not work as expected on all platforms (especially Vercel). Stick to practical intervals like 1 day (86400 seconds) or less.

2. Always Check Response Headers

If you see content-type: text/xml or content-length: 0 from a Next.js route that should return JSON, suspect a caching or revalidation issue.

3. Debug Both API and Frontend

Don’t assume the bug is in your API. Sometimes, the frontend framework’s caching layer is the culprit.

4. Use Tag-Based Revalidation for Manual Control

If you want to serve stale data until a specific event (like a new user creation), use tag-based revalidation:

// Fetch with tag
const response = await fetch("https://api.jsonplaceholder.com/v1/users", {
  next: { tags: ["users"] },
});
 
// In your action or API route
import { revalidateTag } from "next/cache";
await revalidateTag("users");

Conclusion

Caching and revalidation are powerful features in Next.js, but they can introduce subtle bugs if not configured carefully. If you’re building a project like FounderSignal or any data-driven app, test your caching strategy in production-like environments and avoid extremely long revalidation intervals.

Have you faced similar issues? Share your story or questions below!


Happy coding, and may your caches always be fresh!

Frequently Asked Questions

Why is Next.js fetch revalidation returning empty data on Vercel?

Setting revalidate to an extremely large number (like 31536000) or Infinity can cause Vercel's Next.js server to cache and re-serve a broken response after the initial fetch. Cap the revalidate value to avoid this bug.

What is the maximum safe value for Next.js fetch revalidate?

There is no official maximum, but in practice values beyond a few hours (e.g. 3600 seconds) can trigger caching edge cases on Vercel. Use revalidatePath or revalidateTag for long-lived data instead.

What does content-type text/xml with empty body mean in Next.js?

It usually means the fetch cache returned a corrupted or empty cached entry. This can happen when extremely long revalidation windows are set and the underlying cache entry is malformed.

How do I debug Next.js fetch caching issues?

Check the x-nextjs-cache response header (HIT, MISS, STALE) and add logging around your fetch calls. Compare local and deployed behavior to identify cache-related discrepancies.

GitHub
LinkedIn
X