Your Dynamic Rendering Setup Is Showing Googlebot Stale Content
On this page
Dynamic rendering serves crawlers a prerendered HTML snapshot while visitors get the live, client-rendered page. When the snapshot cache lags behind your data, Googlebot receives yesterday’s price and availability while shoppers see today’s. The fix has three parts: purge the cache when search-relevant data changes, check what bots receive, and plan the move off dynamic rendering. Google’s own page on the technique is titled Dynamic rendering as a workaround, and its documentation navigation marks that page as deprecated.
How the stale window forms
A prerender service that caches its output stores a rendered snapshot of each URL it renders and serves it to bots until the entry is refreshed. How and when it refreshes depends on the service and its configuration, so start by finding out what yours does. One arrangement is a time-to-live: the entry expires after a set period and is re-rendered afterward.
The trap is in the timing. If your service re-renders an expired entry only when the next bot request arrives, that request can still receive the old snapshot, and only a later request sees fresh data. Whether the service renders again on request or on a schedule, a time-based expiry leaves a window in which the snapshot and the data disagree.
Shortening the TTL shrinks that window without closing it, and on URLs that bots request steadily, every expiry brings another render, so compute cost rises as the window narrows.
Purge on change, including edits
To close the window, stop relying on time. Connect every search-relevant change to the prerender service’s purge mechanism: a price change, a stock change, a new title or description, a new primary image. Purge that URL’s snapshot so the next request renders from current data.
A gap to check for: purging only when a product is created or deleted. Edits to existing products are exactly when prices and availability change. Audit your purge hooks for update events specifically.
Check what bots receive
Don’t assume the snapshot is fresh; fetch it.
- Your renderer’s output. Request the URL with a Googlebot user agent to see what your routing sends to bots, and compare the key fields with the live page and your database. If your setup verifies bots by IP rather than user agent, a spoofed request won’t reach the bot path; use the renderer’s own preview or cache inspection instead.
- Google’s own fetch. Run a live test in the URL Inspection tool and compare the HTML it shows with your database values.
- A drift monitor. On a schedule, sample important URLs, fetch the bot version, compare price, availability, title and description with the source, and alert on differences. On the URLs it samples, it catches a purge hook that quietly stopped firing, which might otherwise surface only when results degrade.
Keep the purpose of the comparison clear. Google’s dynamic rendering page says Googlebot generally doesn’t consider dynamic rendering cloaking as long as it produces similar content, but that using it to serve completely different content to users and crawlers can be considered cloaking. A snapshot that lags by a price or stock update isn’t the completely different content that line describes, but it is the gap the check exists to close: the two versions should match.
Plan the exit
Google’s page describes dynamic rendering as a workaround for sites whose JavaScript-generated content isn’t available to search engines, and points instead to server-side rendering, static rendering or hydration. Those approaches also help here, because they take the separate bot cache out of the freshness path and give bots and visitors the same HTML:
- Server-side rendering builds current HTML for each request, so there is no bot snapshot to go stale.
- Static generation with on-demand regeneration rebuilds pages when their data changes, so served HTML follows the source without a TTL window. Time-based regeneration reintroduces a window, though one that users and bots share.
Until the migration is done, a tiered TTL is a reasonable stopgap: purge-on-change or very short lifetimes for templates with volatile fields such as price and stock, longer lifetimes for templates with stable copy. Treat it as a way to manage the window, not as the destination.
Prioritize by how fast the data moves
The cost of staleness rises with how quickly your data changes and how much the search result relies on it. A documentation page that changes a handful of times a year can tolerate a long cache lifetime. A product page with moving prices can’t: every hour of lag is an hour in which Google can capture a price that no longer holds.
Map your templates by volatility. Point purge hooks and the drift monitor at the volatile templates first, and accept longer windows on stable ones. The aim is not instant freshness everywhere; it is that the pages where freshness changes the result track their source, and that you know when one drifts.
Frequently asked questions
Is shortening the cache TTL enough?
No. A shorter TTL shrinks the stale window but doesn’t close it, and it can raise render cost. Purging each URL when its search-relevant data changes is what closes it.
Should I keep dynamic rendering if it works for now?
Keep it accurate while you plan the exit. Google’s page on dynamic rendering offers three replacements, server-side rendering, static rendering and hydration, and Google’s documentation navigation marks the page as deprecated.
Is a stale snapshot cloaking?
Not in itself. Google’s line is drawn at completely different content for users and crawlers, and a stale price isn’t that. It is still a mismatch between what Google records and what visitors see, so purge and monitor to keep the versions in step.