Time based fetch-cache revalidation 15.5.15
Unanswered
Goldstripe sardinella posted this in #help-forum
Goldstripe sardinellaOP
Hey, I’m debugging a Next.js 15 App Router caching issue and want to check what I’m missing.
We have a dynamic ad page that fetches ad data from an external API with time-based revalidation:
(simplified fetch)
await fetch(url, {
cache: 'force-cache',
next: { revalidate: 60 }, // normally 15 min, 60s for testing
});
The page also has:
export function generateStaticParams() {
return [];
}
Flow:
User visits an ad page while the ad exists.
API returns 200 with JSON data.
Next creates a .next/cache/fetch-cache entry with the correct URL, status: 200, revalidate: 60, etc.
Later the ad is removed from the API.
The same API endpoint now returns 204 No Content with an empty body.
In our fetch wrapper, 204 returns null.
In page.tsx, null calls notFound().
Expected: after the revalidate window, background revalidation gets the 204, updates/invalidates the old fetch-cache entry, and the page eventually renders as notFound() / 404.
Actual: the old cached 200 response seems to remain in the Data Cache. The page keeps rendering old ad data, and the fetch-cache entry still has status: 200.
If I bypass the cache, the API correctly returns 204 and the page calls notFound(), so the page logic works.
Is this expected with time-based revalidation? Does 204 No Content count as failed/non-cacheable background revalidation, causing Next to keep the previous cached 200 response? What’s the best approach if we still want to use time-based revalidation here?
We have a dynamic ad page that fetches ad data from an external API with time-based revalidation:
(simplified fetch)
await fetch(url, {
cache: 'force-cache',
next: { revalidate: 60 }, // normally 15 min, 60s for testing
});
The page also has:
export function generateStaticParams() {
return [];
}
Flow:
User visits an ad page while the ad exists.
API returns 200 with JSON data.
Next creates a .next/cache/fetch-cache entry with the correct URL, status: 200, revalidate: 60, etc.
Later the ad is removed from the API.
The same API endpoint now returns 204 No Content with an empty body.
In our fetch wrapper, 204 returns null.
In page.tsx, null calls notFound().
Expected: after the revalidate window, background revalidation gets the 204, updates/invalidates the old fetch-cache entry, and the page eventually renders as notFound() / 404.
Actual: the old cached 200 response seems to remain in the Data Cache. The page keeps rendering old ad data, and the fetch-cache entry still has status: 200.
If I bypass the cache, the API correctly returns 204 and the page calls notFound(), so the page logic works.
Is this expected with time-based revalidation? Does 204 No Content count as failed/non-cacheable background revalidation, causing Next to keep the previous cached 200 response? What’s the best approach if we still want to use time-based revalidation here?
1 Reply
Goldstripe sardinellaOP
I also tested deleting the fetch-cache entry for one specific ad. After rebuilding and restarting the app, that ad correctly returned 404. However, other ad IDs that still have old fetch-cache entries continue to render the stale cached data instead of returning 404, even though the API now returns 204 for them.