Server-Side Rendering for SEO: Google Calls Dynamic Rendering a Workaround, and the Parity Arithmetic Says Why
· Royking Niba
The rendering argument has been settled in Google’s documentation for a while and most SEO advice has not caught up. Google no longer recommends dynamic rendering, the user-agent-sniffing approach that serves crawlers a pre-rendered copy. Its own page on the subject calls it a workaround and names server-side rendering, static rendering and hydration as what to use instead. Below are those sentences verbatim, a matrix of the five strategies against what Googlebot actually has to do to see your content, and the parity arithmetic that explains why the recommendation changed.
What Google now says about dynamic rendering
Quoted from Google Search Central’s page on dynamic rendering, last updated 10 December 2025, checked today.
Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.
On what dynamic rendering was for
Instead, we recommend that you use server-side rendering, static rendering, or hydration as a solution.
On what to do instead
Dynamic rendering is a workaround and not a recommended solution, because it creates additional complexities and resource requirements.
On why
Three sentences, and the word “workaround” appears in two of them. Note what the stated reason is not. Google does not say dynamic rendering is penalised, or that it counts as cloaking, or that it stops working. It says the approach creates additional complexities and resource requirements. That is an engineering objection, and it is a correct one, which is why the rest of this page is arithmetic rather than policy.
The five strategies, defined by the people who named them
These definitions are quoted from the rendering-on-the-web article on web.dev by Addy Osmani and Jason Miller, published 6 February 2019 and last updated 5 January 2026.
Rendering an app on the server to send HTML, rather than JavaScript, to the client.
Server-side rendering
Rendering an app in a browser, using JavaScript to modify the DOM.
Client-side rendering
Running client-side scripts to add application state and interactivity to server-rendered HTML.
Hydration
Lets you send HTML in chunks that the browser can progressively render as it’s received.
Streaming server-side rendering
The article is also candid about what each one costs, which is the part that rarely makes it into an SEO recommendation. On server-side rendering: “generating pages on the server takes time, which can increase your page’s TTFB.” On static rendering, it achieves “a fast FCP, and also a lower TBT and INP” plus “a consistently fast TTFB, because the HTML for a page doesn’t have to be dynamically generated.” On client-side rendering: “the amount of JavaScript required tends to grow as an application grows, which can impact a page’s INP.” And the warning that matters most, on rehydration, that it has a “significant negative impact on TBT and INP, even if it improves FCP.”
On indexability the article’s position is one sentence: “Server-side rendering is a popular choice for delivering a ‘complete looking’ experience that crawlers can interpret.” That is the whole SEO case, and it is a modest claim rather than a promise.
The matrix: what Googlebot has to do before it sees your content
The first two columns follow from the definitions above. The rest is the practical consequence for a crawler, and the last column is the one the documentation’s “additional complexities” sentence is pointing at.
| Strategy | Primary content in the first response | Links crawlable without rendering | Needs the render pass | Google’s current position |
|---|---|---|---|---|
| Static rendering | Yes, built ahead of time | Yes | No | Recommended by name |
| Server-side rendering | Yes, built per request | Yes | No | Recommended by name |
| SSR plus hydration | Yes for content, scripts add interactivity after | Yes | No, for content | Recommended by name |
| Dynamic rendering | Only for a user agent the server recognises | Only for a recognised user agent | Yes, for anything unrecognised | Called a workaround |
| Client-side rendering | No | No | Yes, for everything | Not recommended for this problem |
| Five strategies | Three deliver content on the first fetch | Three need no rendering to find links | Two depend on the render queue | Three recommended, one deprecated in effect |
The dynamic rendering row is the only one in the table whose answer to any question is “it depends what the request looked like”. Every other strategy gives the same answer to every client. That conditional is the complexity Google is describing, and it is measurable.
The parity arithmetic: dynamic rendering doubles the thing you have to get right
This is the original dataset on this page. Take a 10,000 URL site and count the rendered outputs that have to exist and stay consistent with each other under each strategy.
| Strategy | Outputs per URL | Outputs sitewide | Pairwise parity comparisons needed |
|---|---|---|---|
| Static rendering | 1 build output | 10,000 | 0 |
| Server-side rendering | 1 server output | 10,000 | 0 |
| SSR plus hydration | 1 server output plus a client state payload | 10,000 | 0 |
| Dynamic rendering | 1 client-rendered output and 1 pre-rendered output | 20,000 | 10,000 |
| Client-side rendering | 1 client output | 10,000 | 0 |
| Dynamic rendering against any other | 2 against 1 | Twice the outputs | 10,000 comparisons that otherwise do not exist |
Four of the five strategies produce one version of a page, so the question “does the crawler see what the user sees” cannot be asked, let alone answered wrongly. Dynamic rendering produces two, which creates 10,000 pairwise comparisons that have to stay equal through every release. There is no report that tells you when one of them drifts.
And the detection problem, which has got worse since 2019
Dynamic rendering decides which of the two outputs to serve by matching the request’s user agent. Every request that fails to match falls through to the client-rendered version, and a crawler that lands on the client-rendered version sees whatever the first response contains, which under this architecture is close to nothing until the render pass.
| Share of crawler requests the pattern list fails to match | URLs of 10,000 served the client-rendered version to a crawler |
|---|---|
| 0%, a perfectly maintained list | 0 |
| 2% | 200 |
| 5% | 500 |
| 10% | 1,000 |
The reason that failure rate is not zero in practice is that the list of crawlers a publisher now wants to serve has grown. Googlebot, Googlebot-News and Googlebot-Image, Bingbot, Applebot, and then the newer group: GPTBot, OAI-SearchBot, ChatGPT-User, PerplexityBot, ClaudeBot. Ten families, each with its own string and each free to revise that string. A list that handled one crawler in 2019 now has roughly ten entries to keep current, and over three years of revisions that is on the order of thirty patterns maintained by hand, each of which is one typo away from sending a search engine the empty version of your site.
What this model assumes
The output counts are exact arithmetic on a stated site size and do not depend on any estimate. The detection-failure table is a sensitivity range, not a measurement: I do not know what share of crawler requests any given pattern list misses, and nor does anyone without the server logs. The ten crawler families and the thirty-patterns-over-three-years figure are a count of what a publisher may reasonably want to serve, not a requirement. The conclusion stands on the exact part: one architecture asks you to keep two outputs identical across 10,000 URLs with no report to tell you when they diverge, and four do not.
How I advise on this
- If the content is the same for everyone and changes rarely, use static rendering. It is the only strategy that wins on TTFB, FCP and indexability at the same time, and web.dev says so in the quotes above.
- If the content is personalised or changes per request, use server-side rendering and accept the TTFB cost, which web.dev names explicitly rather than hides.
- If the page needs real interactivity, server-render the content and hydrate, but treat the hydration payload as a performance liability. The documented warning is a “significant negative impact on TBT and INP, even if it improves FCP”. A page that looks ready and is not is a worse experience than one that looks slow.
- Do not build new dynamic rendering. Google’s own page calls it a workaround twice and names three alternatives. If you have inherited one, the migration target is whichever of the three fits the content, and the interim job is a parity check between the two outputs.
- Do not treat rendering strategy as an SEO deliverable on a site that already server-renders. It is not a lever you can pull twice, and on most sites the crawlable-links problem is a bigger win than the rendering one.
The thing to hold onto is that Google’s objection to dynamic rendering is about maintenance, not about rules. It stopped recommending the approach because keeping two versions of a site identical forever is a job nobody resources after the launch, and the arithmetic above is what that job looks like on an ordinary ten thousand page site.
Related reading
- Google Penalty Recovery: how I diagnose and reverse a traffic collapse
- JavaScript SEO: the three phases Googlebot uses, and the discovery cost of rendering your links
- Page experience: Google says there is no single signal, and the 75th percentile is why that matters
- Cloaking in SEO: what counts, what does not, and how Google catches it
- Crawl errors: what Google does with each response code
- hreflang implementation: Google says the three methods are equivalent
Leave a Reply