ssrchecker
HomeCheckFrameworksNuxt

Is your Nuxt page actually server-rendered?

Short answer
Nuxt server-renders by default, so most Nuxt pages return complete HTML. A page comes back client-rendered mainly for three reasons: ssr: false in the config, content wrapped in <ClientOnly>, or data fetched in onMounted instead of useAsyncData. The default is the good case.

Nuxt is the inverse of plain Vue. A bare Vue app is client-only and the question is whether you have server rendering at all; Nuxt gives you server rendering by default, so the question becomes which specific choice turned it off for a given page. That makes the Nuxt diagnosis subtler — the page is rarely empty, it is a server-rendered page with the important part missing, which reads HYBRID.

Check your Nuxt page
https://

The ssr:false switch turns off everything

The single most common reason a Nuxt app comes back CSR is one line in nuxt.config:

export default defineNuxtConfig({ ssr: false, // <- turns the whole app into a client-only SPA })

With ssr: false, Nuxt behaves exactly like a default Vue SPA: the server sends an empty shell and everything renders in the browser. This is sometimes set deliberately — for an internal dashboard behind a login, it is a reasonable choice — but it is also set by accident, or inherited from a starter template, and then forgotten. If your whole site comes back CSR, check this line first.

The <ClientOnly> trap

Nuxt provides a <ClientOnly> component that renders its children only in the browser, skipping them during server rendering. It exists for good reasons — wrapping a component that touches window, or a third-party widget that breaks under SSR.

The trap is wrapping too much. If your main content — an article body, a product grid, a listing — sits inside <ClientOnly>, it is deliberately excluded from the server HTML, so crawlers never see it. The rest of the page renders server-side and looks healthy, which produces a HYBRID verdict that is easy to wave away. Anything a crawler needs to read must live outside <ClientOnly>.

useAsyncData and useFetch vs fetching in onMounted

This is the most frequent subtle cause, and the fix is usually a small refactor rather than an architectural change.

SSR-safe

useAsyncData and useFetch run on the server and block the render until the data resolves, so the content is in the HTML the crawler receives.

Client-only

A bare $fetch in a component, or a fetch inside onMounted, runs only in the browser after hydration. The content is absent from the server HTML.

The two look almost identical in a component, which is why this slips through. Both fill the page correctly in a browser. Only the first puts the data in the server response. Moving a client-side fetch into useAsyncData is often the entire fix for a HYBRID Nuxt page.

Reading the hydration payload

Nuxt leaves a useful tell in the HTML. A server-rendered Nuxt page embeds a hydration payload — a script block carrying the state the server resolved, so the client can hydrate without refetching. Its presence is a positive signal that the page was rendered on the server.

What matters for diagnosis is the relationship between that payload and the visible HTML. If the payload is present and the content is in the HTML, the page is genuinely server-rendered. If the shell is there but your main content is missing from both the HTML and the payload, that content is being fetched client-side — which points straight back to the onMounted pattern above. The checker compares raw and rendered output so this gap is visible without reading the payload by hand.

Side-by-side comparison of a Nuxt page's raw HTML with the article body present versus a HYBRID case where the body is missing before hydration
A HYBRID Nuxt result — layout and payload present, the main content fetched too late.

Per-route rendering rules

Nuxt can apply different rendering strategies per route through route rules, which means one deployment can legitimately return different verdicts on different URLs. A marketing route might be prerendered, a dashboard route might be ssr: false, and a frequently-updated route might use stale-while-revalidate caching.

This is by design, not a bug — but it means you should check the templates that need to rank individually rather than assuming the whole site shares one mode. A green homepage tells you nothing about a product route governed by a different rule.

Your configuration and the verdict to expect

Find your setup. If the tool returns something other than the expected verdict, that difference is the bug.

SetupExpectWhy
Default Nuxt app (ssr not set)SSRSSR is on by default. Pages render to complete HTML on the server.
nuxt generate (full static / SSG)SSREvery route prebuilt to HTML. Crawlers receive everything.
Page data via useAsyncData or useFetchSSRThese run on the server and block the render until data resolves.
routeRules with prerender or swr for a routeSSRThat route is served as cached HTML. Crawlers get full content.
ssr: false in nuxt.configCSRTurns the entire app into a client-only SPA. Empty shell in the HTML.
routeRules with ssr: false for a route patternCSRThose routes are client-only by design, even though the rest is SSR.
Main content wrapped in <ClientOnly>HYBRIDContent inside ClientOnly is intentionally excluded from the server HTML.
Data fetched in onMounted or plain $fetch in a componentHYBRID or CSRRuns only in the browser after hydration; not in the server HTML.

Nuxt rendering FAQ

Yes. A default Nuxt application server-renders every page to complete HTML unless you change that. This is the opposite of plain Vue, which is client-only by default. A Nuxt page usually comes back client-rendered only when ssr is set to false, content is wrapped in ClientOnly, or data is fetched client-side after hydration.