There isn't one right answer here, whatever a framework's marketing page tells you. There are three broad rendering families - full server-side rendering, static generation, and bot-targeted dynamic rendering - plus hybrid combinations that mix all three at the route or even component level in modern frameworks. Which one fits depends on what your content actually needs (static, periodically refreshed, or genuinely per-request) and how much of your build pipeline you can touch this quarter, not on which approach is theoretically "best." If you haven't confirmed you actually have this problem yet, run the self-test first - everything below assumes you already know GPTBot, ClaudeBot, or PerplexityBot are getting an empty shell where your content should be.

The Three Rendering Families

Full server-side rendering (SSR) generates HTML on the server when a cache can't satisfy the request, so crawlers and browsers both get complete HTML - the most thorough option, and traditionally the most engineering work, though teams commonly add server- or CDN-level caching on top rather than rendering fresh on literally every hit. Static generation (SSG) builds pages ahead of request time into plain HTML - cheap to serve, and it avoids per-request application rendering, but on its own it can't include data specific to a signed-in user, and it still carries real build, storage, delivery, and cache-invalidation costs. Dynamic rendering via a prerendering service sits in front of your existing client-side app and serves a separately generated snapshot specifically to bots, while humans keep getting the normal client-rendered experience - the least invasive option for your codebase, and the fastest to stand up, though it isn't free of ongoing engineering and operational work, and it's the one family search engines actively steer you away from (more on that below). These are three broad families, not a single site-wide choice: modern frameworks let you mix static, cached/revalidated, and request-time rendering per route - sometimes per component - inside one application. More on that below.

Full Server-Side Rendering

If you're already on Next.js, Nuxt, or Angular (via the @angular/ssr package - Angular's current server-rendering tooling, and the successor to the older "Angular Universal" project, which was folded into Angular core starting with Angular 17), turning on SSR for a given route is typically a configuration and refactoring project, not a full rewrite - these frameworks support server rendering natively, in Angular's case at the route level via RenderMode.Server. If you're on a plain React or Vue SPA with no SSR-capable framework underneath it, this is a real migration: routing, data fetching, and component logic all need to work on the server, not just the client. SSR is the right call for content that must reflect the specific request - session state, personalization, live data that can't be cached - but it isn't inherently more "permanent" than the alternatives. Static generation, ISR/SWR-style revalidation, and hybrid rendering are all documented as first-class long-term architectures in these frameworks too; choose based on freshness needs, personalization, and who owns the ongoing operational upkeep, not a blanket label of "proper fix" versus "workaround." Server-side rendering does carry its own ongoing cost worth planning for: your server runs the framework to produce HTML on every request that isn't served from cache, which is a real, recurring hosting expense, not a one-time build cost.

Static Generation

For content that's genuinely the same for every visitor - most marketing pages, docs, blog content, comparison pages - SSG is usually the cheapest fix by a wide margin: build it once at deploy time and skip per-request application rendering entirely. That's not the same as zero cost - hosting, bandwidth, cache invalidation, and rebuild time are all real, and a large number of prerendered pages can itself slow down your build and deployment - and "build once, serve forever" oversimplifies it: ISR and stale-while-revalidate approaches (available in Next.js and Nuxt, among others) let you regenerate specific pages on a schedule or on demand without a full site rebuild, so "static" doesn't have to mean "frozen until the next deploy." Plain SSG still breaks down fast for anything that varies by user, account, or query parameter (a logged-in dashboard, personalized pricing, search results) - by definition, a page built once at deploy time can't include data specific to the user loading it, so that's a job for request-time rendering, not your marketing site's build pipeline.

Dynamic Rendering / Prerendering-as-a-Service

This is the fastest fix if you can't touch your rendering pipeline this quarter: a service like Prerender.io sits between your server and incoming requests, detects when a bot is asking, and serves it a pre-rendered snapshot instead of the raw client-side shell - humans still get your normal app untouched. Prerender.io's current pricing runs from $49/month for 25,000 renders up to $349/month for 500,000, with custom Enterprise Plus pricing above that, as of this writing.

Know this going in, not as a footnote: Google's own dynamic-rendering documentation doesn't just warn about cloaking - it states plainly that "dynamic rendering is a workaround and not a recommended solution, because it creates additional complexities and resource requirements," and directs sites toward server-side rendering, static rendering, or hydration instead. That's Google's current, general position on the whole approach, not a caveat limited to misuse. Plenty of teams still reach for a prerendering service as a fast bridge when the alternatives genuinely aren't feasible this quarter - and that's a legitimate reason to use one - but go in knowing it's the option search engines actively steer you away from, not a peer of SSR and SSG with a single cloaking condition attached. One honest warning on top of that: whatever service or example you're evaluating here, verify it's still active and maintained before you build around it - Google's own Rendertron project, once the default reference for this approach, was archived in October 2022, and Google's own README on it says plainly that "dynamic rendering is not a recommended approach" going forward. If a guide or vendor is still pointing you to Rendertron, that's a sign the advice hasn't been updated in years.

There's an ongoing obligation that comes with this option too, not just a setup cost: Google treats bot-targeted dynamic rendering, when a team chooses to use it anyway, as generally acceptable only when the crawler version stays materially equivalent to what a real visitor sees. Google's own guidance is explicit that serving similar content to bots isn't cloaking, but serving substantially different content can be - its own illustrative example is a site that shows users a page about cats and crawlers a page about dogs. In practice, that means treating the prerendered snapshot as a monitored surface, not a one-time setup: keep primary text, links, metadata, canonical directives, and structured data in sync with the live page, and re-check representative routes after releases and cache refreshes, not only at initial setup. Don't use the crawler-only response to add claims or content that a real visitor can't also reach.

Hybrid Rendering: Mixing Static, Cached, and Request-Time Content

The frameworks behind the first two families don't actually force an all-or-nothing choice anymore. Next.js, Nuxt, and Angular can each mix static, cached/revalidated, and request-time rendering within a single application - in some cases within a single route. Next.js's current caching model lets a route ship a static shell for most of the page while one component streams in request-time data behind it; Nuxt's route rules let you set prerender, swr (stale-while-revalidate), isr, or ssr per route; Angular's server-routing config sets RenderMode.Client, RenderMode.Server, or RenderMode.Prerender per route. In practice, this means the real decision usually isn't "which single mode does the whole site use" - it's which parts of a given page are safe to serve static or cached, and which parts genuinely need to be computed at request time.

One wrinkle worth knowing about if you're on Next.js specifically: its current caching documentation states that bots and crawlers are detected by user agent and handled differently from browsers - because a crawler needs a complete document, Next.js skips the static shell and renders the entire page dynamically at request time for it instead, then sends the finished HTML once the render completes. That's framework-level behavior you don't have to build yourself, but it's worth confirming against your own Next.js version and access logs rather than assuming it applies identically across every deployment and route configuration - and it's also worth noting that any part of your shell that depends on build-time-only data can fail to render for a bot precisely because that request-time render path skips the cached shell it would otherwise reuse.

The Decision Framework

Four questions, in order:

  1. Does the content need to be identical for everyone, refreshed periodically, or genuinely different on every request? Content that's the same for every visitor is a strong candidate for static generation. Content that needs to stay current but doesn't change every second - pricing pages, catalogs, blog indexes - often fits ISR/SWR-style scheduled or on-demand revalidation instead of full per-request rendering. Content that must reflect the specific request - an authenticated user's data, session state, anything genuinely personalized - needs request-time server rendering, or a hybrid route/component that renders just that part at request time. A prerendering service's cached crawler snapshot is not a substitute for this: it's a public, generic representation built for bots, not a stand-in for authenticated or user-specific output. Don't route personalized content to a bot-only snapshot.
  2. Are you already on an SSR-capable framework (Next.js, Nuxt, Angular)? If yes, that's a real advantage - turning on route-level SSR, SSG, or ISR is often less work than adopting and operating a separate service, since the tooling and deploy pipeline already exist. It isn't automatically the cheaper path in every case, though: routes that depend on browser-only APIs, client-side auth state, or libraries that don't run server-side can make even a "supported" migration substantial. Estimate the route-scoped cost before assuming it's the cheap option.
  3. How much engineering time do you actually have this quarter? If the honest answer is close to zero and a framework-level migration isn't realistic soon, a managed prerendering service is usually the fastest bridge to stand up - but remember it's the option Google's own documentation calls a workaround rather than a recommended solution, not a neutral third choice. It isn't a zero-effort option either: you still need to implement crawler detection and routing, own cache-freshness rules, and validate content parity between what bots and users receive (see the cloaking guardrail above). Budget for that ongoing ownership and the search engine's disfavor toward it, not just the subscription cost.
  4. Is this a permanent fix or a bridge? Prerendering services can be a workable stopgap for some teams, but given that search engines themselves recommend against relying on the approach long-term, if the plan is "fix it properly eventually," say so explicitly and revisit on a schedule - a bridge that nobody revisits becomes permanent infrastructure by accident.

The SaaS-Specific Wrinkle: Marketing Site vs. Gated App

Most SaaS companies run two very different surfaces under one domain: a marketing site (pricing, features, docs - the part AI crawlers actually need to read) and a logged-in app behind authentication (which crawlers can't reach anyway, and don't need to). Don't spend SSR migration effort on your app's authenticated routes - no crawler is getting past your login wall regardless of rendering method. Scope this fix to what's actually public: your marketing pages, docs, and any public-facing content. That scoping alone often turns a company-wide rendering migration into a much smaller, marketing-site-only project.

What to Do Next

← Back to the full pillar page
Not sure why this is happening in the first place? →
The implementation checklist, once you've picked an approach →
Should llms.txt be part of this project too? →


Sources: Prerender.io pricing as listed at prerender.io/pricing/ as of this draft (August 2026) - check current pricing before quoting it to a client, as SaaS pricing changes without notice. Rendertron's archived/deprecated status and its own "not a recommended approach" statement are documented at github.com/GoogleChrome/rendertron, archived October 6, 2022. Google's current position that dynamic rendering "is a workaround and not a recommended solution," its recommendation of server-side rendering, static rendering, or hydration instead, and its cloaking guardrail (including the cats-vs-dogs example) are from Google Search Central's dynamic rendering documentation. Framework-specific rendering details are from Angular's server-side rendering guide (route-level RenderMode options and the personalized-route example), Angular's SSR performance guide (prerendering overhead and build-cost notes), Next.js's caching documentation (static/cached/request-time mixing and bot-specific rendering behavior), Next.js's ISR guide, and Nuxt's rendering-modes documentation (route rules for prerender, SWR, ISR, and SSR). Framework documentation gets reorganized and re-titled periodically - the URLs above were current as of this revision (August 2026); if a link has moved, search the framework's own docs site for "caching," "rendering," or "SSR" rather than trusting a stale third-party summary.

About the author

Zarko Zivkovic is the founder of CoreAEX, building technical SEO, AEO, and AI-visibility systems for B2B SaaS companies. Connect on LinkedIn.