Why does Next.js ISR (Incremental Static Regeneration) sometimes serve stale content to Googlebot while users see updated pages, and how does this affect indexing accuracy?

The stale response happens because of a timing gap built into how ISR is designed to work: the request that finally arrives after a page’s revalidation window has expired doesn’t get the regenerated version, it gets the old cached version one more time, and it’s that request which triggers background regeneration for whichever request comes next. If Googlebot’s crawl happens to be the request that lands in that gap, Google indexes the stale content, and the fresh version isn’t captured until Googlebot (or any crawler) happens to request the page again after regeneration has actually completed, which isn’t a guaranteed or scheduled event.

This is a direct consequence of ISR’s stale-while-revalidate model, not a bug in a particular implementation. Next.js’s own documentation describes the behavior plainly: once the configured revalidation interval has passed, the next request to that page still returns the existing cached HTML (now considered stale), while Next.js triggers a regeneration of the page in the background. Only once that regeneration finishes does the updated version get cached and served, and it’s served starting with the request after the one that triggered the rebuild, not the triggering request itself. This is what makes ISR fast and resilient for human traffic (nobody ever waits on a synchronous rebuild), but it also means whichever request happens to be “first through the door” after expiration receives outdated content by design.

Why this happens: the mechanics of the regeneration race

ISR’s whole value proposition rests on avoiding a rebuild-on-every-request model. Instead of regenerating a page synchronously whenever content changes, Next.js pre-renders pages at build time or on first request, caches the result, and reuses that cached HTML for a configured period (the revalidate interval, when set). During that period, every request is served instantly from cache regardless of whether the underlying data has changed.

Once the revalidation window closes, the cache entry is considered stale, but Next.js doesn’t preemptively refresh it. It waits for a request to arrive. When one does, that specific request still gets the stale cached page (so the requester experiences no rebuild delay), and that request is what kicks off regeneration in the background. Whoever asks for the page next, after regeneration finishes, is the first to actually receive the updated version.

Under normal human traffic patterns, this gap is invisible. Popular pages get requested constantly, so the “stale” request and the “first fresh” request are typically separated by a fraction of a second, and no one perceives the lag. The problem shows up specifically because crawler traffic doesn’t behave like a constant stream of human visitors. Googlebot’s crawl scheduling for any given URL is Google’s own decision, based on factors like perceived change frequency, crawl demand, and site-wide crawl budget, and Google has been consistent that recrawl timing after a page changes isn’t fixed or guaranteed to happen quickly. If Googlebot’s one crawl of that URL for the relevant period happens to land in the window right after expiration, it receives the stale page, triggers the rebuild, and then may not return again for some unpredictable interval, during which Google’s index reflects the outdated version rather than the current one.

This is compounded on pages with infrequent traffic, long revalidation windows, or programmatic pages generated in bulk (product pages, listings, or other data-driven templates), because those are exactly the pages where a long stretch can pass with no human request at all, meaning the “stale, then trigger rebuild” event is disproportionately likely to be a crawler’s request rather than a user’s, simply because there’s nothing else hitting that URL to absorb the stale response first.

The indexing accuracy consequence is straightforward: Google indexes what it was served at crawl time. If that happened to be the stale response, the index reflects out-of-date price, availability, article content, or whatever else changed, until the next crawl happens to land after a completed regeneration. For time-sensitive content (pricing, inventory, breaking updates to an article) this can create a meaningful and hard-to-detect gap between what’s actually true on the live site and what Google has indexed.

How to reduce ISR staleness risk for indexing-sensitive pages

Use on-demand revalidation for content where staleness has real consequences. Since Next.js version 12.2, on-demand revalidation lets you explicitly purge and regenerate a specific page’s cache the moment content actually changes (for example, by calling a revalidation API route from your CMS webhook or publishing workflow), rather than waiting for a fixed time interval to lapse and hoping the next request isn’t the one that matters. This removes the guessing game around interval length entirely for content where you control the publish event: the cache is invalidated at the moment of the actual change, not on an arbitrary clock separate from when the update really happened.

Reserve time-based revalidation intervals for content where some staleness is genuinely acceptable. For pages where you don’t have a clean publish-event hook, or where the underlying data changes continuously rather than at discrete moments, a time-based revalidate value is still reasonable, but the interval should be chosen based on how much staleness the specific page can tolerate, not copied uniformly across the whole site. There’s no universally correct number here, since the right interval depends entirely on how often the underlying content actually changes and how costly staleness is for that particular page; treat it as a per-template decision rather than a global default.

Don’t assume Google will recrawl quickly after a rebuild completes. Because Google’s recrawl timing isn’t fixed or guaranteed to happen promptly after any given change, the practical fix for indexing accuracy isn’t to hope Google’s next crawl lands after your regeneration finishes, it’s to reduce how often a crawl can land during the stale window in the first place. Combining on-demand revalidation with a reasonably short time-based fallback interval on volatile pages narrows that window considerably, rather than eliminating it outright.

Monitor what Google actually has indexed versus what your site currently serves, particularly for high-value or fast-changing URLs. Use the URL Inspection tool in Search Console to check the “Live URL” rendered version against the currently cached version in the index for pages you suspect may be affected, especially pages that see low or irregular traffic where the stale-then-regenerate race is statistically more likely to have caught a crawler request rather than a human one. This is the most direct way to confirm whether a specific page is suffering from ISR-related staleness in the index rather than assuming the mechanism is or isn’t at play for a given URL.

Consider whether the page needs ISR at all versus full dynamic rendering. For a relatively small number of pages where content changes very frequently and freshness in search results matters enormously (breaking news, live pricing, time-sensitive inventory), server-side rendering on every request, rather than a cached-and-revalidated model, removes the staleness race altogether at the cost of not having a static cache to serve from. That tradeoff, speed and cache efficiency versus guaranteed freshness, is the underlying decision ISR asks you to make per template, and it’s worth revisiting deliberately for pages where indexing accuracy is unusually consequential.

Leave a Reply

Your email address will not be published. Required fields are marked *