Next.js and Indexing: SSR, ISR, and What Google Sees

Next.js lets you render the same page four different ways, and each way changes what Googlebot receives when it fetches the URL. That is the whole subject of Next.js SEO and indexing in one sentence. The framework’s marketing says “SEO-friendly by default”, and the server-rendered modes mostly earn that. But a Next.js project usually mixes modes: a static marketing page here, an ISR product page there, a dashboard that renders entirely in the browser. Google treats each of those differently, and the differences decide whether a page gets indexed, gets indexed late, or gets indexed as an empty shell.
This post walks through what Google actually sees in each mode, the Next.js settings that quietly block indexing, and how to check the outcome instead of assuming it.
Google renders JavaScript, but on its own schedule
Google processes JavaScript pages in phases: it crawls the URL, parses the initial HTML, and queues the page for rendering in a headless Chromium before final indexing. The JavaScript SEO basics documentation describes this pipeline plainly. The practical consequence: content present in the initial HTML response is available to Google immediately, while content that only exists after client-side JavaScript runs has to wait for the rendering step, and depends on that step succeeding.
Rendering can fail quietly. If your JavaScript bundles are blocked by robots.txt, if a fetch to your API errors out, or if content only appears after a click or a scroll, the rendered page Google evaluates is missing the material you wrote. Google does not report this as an error. The page is simply judged on what rendered.
That is why the rendering mode you choose in Next.js matters so much: it decides how much of your page survives without that second step.
Next.js SEO and indexing, mode by mode
Server-side rendering (SSR). With getServerSideProps in the Pages Router, or Server Components in the App Router, the HTML that leaves your server already contains the content. Googlebot gets the full page on the first fetch, no rendering queue required for the main content. This is the safest mode for indexing. The thing to watch is response time: SSR pages are generated per request, and a slow or erroring origin during a crawl serves Google a slow or erroring page.
Static site generation (SSG). Pages built at deploy time are plain HTML files. Bots and humans get identical, complete responses. Indexing-wise this is as good as it gets. The trade-off is freshness: the indexed copy reflects your last build, and nothing changes until you rebuild and the page is recrawled.
Incremental static regeneration (ISR). ISR serves a cached static page and regenerates it in the background after a revalidation window. Two things follow. First, Google sees whatever cached copy your CDN holds at crawl time, which may be older than what your database says. If your revalidation window is long, the indexed version lags reality by that window plus however long Google takes to recrawl. Second, fallback behaviour matters: with fallback: 'blocking' a first-time request waits for full HTML, while fallback: true serves a loading skeleton first. A skeleton is a poor thing to have crawled. Prefer blocking fallbacks for anything you want indexed.
Client-side rendering. A page that mounts and then fetches its content in the browser sends Google an initial response that is mostly <div id="__next"></div> and script tags. Google can render it, and often does, but everything now depends on the rendering step: the queue, the bundle loading, the API responding. Pages like this are heavily represented in “Crawled, currently not indexed” reports. If a page must rank, move its content into the server-rendered HTML.
The Next.js settings that quietly say noindex
Most Next.js indexing failures are not rendering failures. They are instructions the site is sending without realising it.
The metadata robots field. In the App Router, robots: { index: false } inside metadata or generateMetadata emits a noindex tag. This is correct on staging and fatal in production, and it survives copy-paste between environments better than anyone would like. Google honours noindex whether or not you meant it.
Headers. A noindex directive does not have to appear in the HTML at all. headers() in next.config.js, middleware, or the hosting platform can attach an X-Robots-Tag: noindex header to the response. Hosting platforms commonly protect preview deployments this way, which is sensible right up until a preview configuration ends up serving production traffic. The header version is easy to miss because view-source shows nothing; we wrote up how to catch it in the X-Robots-Tag header explainer.
Canonicals. alternates: { canonical } in metadata tells Google which URL is the real one. A canonical pointing at a different domain (an old site, a www variant, a localhost value that escaped) invites Google to index that URL instead of yours.
Middleware redirects. Locale detection that 307-redirects visitors based on headers can redirect Googlebot too. If every request to / bounces to /en, make sure /en is the URL you submit, canonicalise, and expect to see indexed.
Discovery: generate the sitemap you already have the data for
Next.js can build a sitemap from the same data that builds the pages: app/sitemap.ts returns your routes and last-modified dates, and the framework serves the XML. For dynamic route segments (products, posts, listings) this is the difference between Google discovering ten pages and discovering ten thousand. Google’s guidance on building sitemaps applies unchanged; the pillar walkthrough in the Ultimate Guide to Google Indexing covers where sitemaps fit in the wider process.
A sitemap is discovery, though, not indexing. Listing a URL asks Google to look. It does not oblige Google to keep what it finds.
Verify the outcome, not the setup
Everything above is setup. The outcome is a separate fact, and with JavaScript in the mix the gap between the two is wider than usual: the deploy can be green, the rendering can succeed on your machine, and the page can still sit outside the index. Submitted is not indexed, and no framework choice changes that.
So check. This is where a tool like AIndexify fits the Next.js workflow: paste the URLs from your sitemap (or upload the CSV, up to 10,000 URLs per submission), and each URL is checked against live Google results on a schedule, with submitted and indexed reported as two separate events. The preflight also catches the quiet blockers described above: a URL carrying noindex, a robots block, or a 404 comes back as “Couldn’t check”, with the credit refunded, which is often the first hard evidence that a metadata flag or header escaped staging.
Ship the pages server-rendered, keep the robots directives honest, generate the sitemap, and then look at what Google actually did with it. See where your Next.js pages actually stand: submit your URLs and read the results as they come in.