Your Dynamic Rendering Setup Is Showing Googlebot Stale Content

On this page

If you run dynamic rendering, a prerender service caching HTML and serving it to bots, a stale cache will quietly feed Googlebot old prices and availability while your users see live, accurate content. The mechanism is the cache lifecycle: a time-based TTL always leaves a stale window, and the cache only refreshes on the next bot request after expiry, which means the first crawl after the TTL lapses still serves the old snapshot. The real fix is event-driven invalidation that purges the cache the moment any search-relevant field changes, and, more fundamentally, planning your way off dynamic rendering, which Google now classifies as a deprecated workaround rather than a recommended architecture.

This is specifically the “present but stale” failure: Googlebot sees content, it is just outdated. That is distinct from content that never rendered because a scroll trigger did not fire, and from a CDN caching a stale directive like a noindex tag. Here the data is there and wrong, and the cause is the render cache lagging your source of truth.

The staleness cycle and why TTL never closes the window

Walk the lifecycle exactly, because the trap lives in the timing. A bot requests a URL. The prerender service returns its cached HTML, and it returns that cached HTML even if the underlying data has changed since the snapshot was taken. The cache entry has a TTL; when it expires, the entry is stale, but it does not refresh on its own. It refreshes when the next bot request comes in, and that request triggers a fresh render. So the first crawl after expiry is served from the stale entry, and only the crawl after that gets fresh data. You are always at least one bot visit behind.

This is why shortening the TTL is not a fix. A 24-hour TTL is plainly too long for price-sensitive or inventory-sensitive data, but cutting it to an hour only shrinks the stale window; it does not close it, because the next-request-refreshes-it dynamic is unchanged. Shorter TTLs also multiply re-render cost, because every expiry forces a fresh, expensive render on the next crawl. You spend more compute to make the problem smaller without making it go away.

Event-driven invalidation, including the edit gap

The way to actually close the window is to stop relying on time and invalidate on change. Wire every search-relevant change to call the prerender service’s purge API: when a price changes, when availability flips, when a description or a primary image changes, purge that URL’s cached render so the next crawl regenerates it from current data. The cache then reflects truth shortly after the change rather than after a timer.

The gap most teams ship is invalidating only on create and delete. They purge when a product is added or removed, but not when an existing product is edited, which is exactly when prices and availability change. The edit path is the one that matters most for staleness, and it is the one most commonly missed, so audit your invalidation hooks specifically for update events, not just lifecycle creation and deletion.

Verify what the bot actually receives

Do not assume; fetch. Request the URL with a Googlebot user-agent (a curl request with the Googlebot UA is enough to see what the prerender service serves bots) and diff it against the live browser render and against the live database value for the fields you care about. If the prerendered price is week-old and the live price is current, you have confirmed staleness rather than guessed at it.

At scale, turn that one-off check into a drift monitor. Sample your important URLs, fetch the bot-served version, compare key fields (price, availability, title, description) against the source of truth, and alert when they diverge beyond a threshold. That monitor is what catches an invalidation hook that silently stopped firing, which is otherwise invisible until rankings or rich results degrade.

One guardrail while you do this: you are diffing to detect staleness, not to justify serving Googlebot a deliberately different content set. Branching content by user-agent is cloaking, and dynamic rendering is supposed to serve bots the same content users get, just rendered differently.

The architecture call: dynamic rendering is legacy now

Treat dynamic rendering as a state to manage or exit, not a target to build toward. Google’s own documentation reduced dynamic rendering to a workaround and stopped recommending it, pointing instead to server-side rendering, static generation, or hydration, and Search Engine Land covered the change when Google made it. The reasoning is the one this post demonstrates: an intermediate render cache is another system that can lag, break, and silently serve stale content, and it adds operational complexity for a problem the rendering architecture can solve directly.

SSR and SSG remove the intermediate cache from the freshness path. With server-side rendering, the server generates current HTML on request, so there is no separate bot cache to go stale. With static generation plus incremental regeneration, pages are rebuilt when their data changes, so the served HTML tracks the source without a TTL window. Either way, fresh HTML reaches Googlebot without a prerender layer in between. So the recommendation is not “tune your dynamic rendering,” it is “plan the migration to SSR or SSG, and treat the rendering setup you have as a legacy state to keep accurate until you exit it.”

If you are stuck on a custom stack and cannot migrate immediately, a tiered TTL is a defensible stopgap: very short or event-purged TTLs for volatile fields like price and stock, longer TTLs for static content like descriptions and editorial copy. It is a way to manage the window while you build the path off dynamic rendering, not a destination.

Why this matters more for some content than others

The cost of staleness scales with how fast your data moves and how much the search result depends on it. A documentation page that changes a few times a year tolerates a long cache window without consequence, because the stale snapshot is rarely wrong and the SERP does not hinge on a fast-moving field. A product page with shifting price and availability is the opposite: every hour the cache lags is an hour Googlebot may capture a price that no longer holds, and that mismatch can surface in a way that erodes click quality and, where structured data is involved, the accuracy of what Google reproduces.

Prioritize accordingly. Map your URL templates by data volatility, point your event-driven purges and your drift monitor at the volatile ones first, and accept longer windows on the stable ones rather than spreading purge logic evenly across pages that do not need it. The goal is not to make every page instantly fresh; it is to make the pages whose freshness actually changes the search result track their source closely, and to know the moment one of them drifts. That triage is also what keeps the re-render cost manageable while you complete the migration off dynamic rendering entirely.

Frequently Asked Questions

Is shortening the cache TTL enough to fix stale content?

No. A shorter TTL shrinks the stale window but does not eliminate it, because the cache only refreshes on the next bot request after expiry, so the first post-expiry crawl is still served stale. It also raises re-render cost. Event-driven invalidation on every relevant change is what actually closes the window.

Should I keep dynamic rendering if it is working for now?

Treat it as legacy. Google has reclassified dynamic rendering as a workaround and recommends server-side rendering, static generation, or hydration instead. Keep your current setup accurate with event-driven purges, but plan the migration rather than investing in dynamic rendering as a long-term architecture.

Sources

Dynamic rendering as a workaround (Google Search Central): https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering
Google no longer recommends using dynamic rendering for Google Search (Search Engine Land): https://searchengineland.com/google-no-longer-recommends-using-dynamic-rendering-for-google-search-387054