SEO.co
// blog

Next.js SEO Audits for JavaScript Rendering and Indexing Failures

Diagnose Next.js rendering modes, hydration gaps, indexation leaks, and metadata failures with an engineering-led SEO audit framework built for App Router sites.

Timothy CarterTimothy Carter
Next.js SEO Audits for JavaScript Rendering and Indexing Failures

A standard Next.js SEO audit asks whether pages render; a useful one asks which rendering path each route takes, where the metadata actually resolves, and what the crawler sees before JavaScript runs. That distinction is why a clean Screaming Frog report can coexist with a traffic chart that has slipped quarter over quarter.

Next.js 14 and 15 shifted the surface area of technical SEO into the App Router, Server Components, and streaming responses. The failure modes that matter now are not broken links. They are metadata that resolves after the head closes, ISR caches that pin a stale canonical, and middleware redirects that rewrite bots differently from users.

What follows is a diagnostic frame you can use to pressure-test your engineering team or your audit vendor, whether you run the work in-house or through an AI SEO and GEO partner.

Why Next.js Breaks Indexation in Ways a Crawler Misses

The pipeline is well documented. Google processes JavaScript web apps in three phases — crawling, rendering, and indexing — and every page returning a 200 goes to the render queue whether or not it contains JavaScript. That is not the problem. The problem is what Googlebot has in hand before the render queue reaches it, and what other crawlers see at all.

AI answer engines do not wait. A joint analysis by Vercel and MERJ of over 500 million GPTBot fetches found no evidence of JavaScript execution. GPTBot fetched JavaScript files in roughly 11.5% of requests and ClaudeBot in 23.84%, but neither executed the code. If your product page depends on a client component to render its title, description, price, or FAQ schema, ChatGPT and Claude see an empty shell. Google eventually renders it; the AI engines never do.

That asymmetry is the single most important thing to carry into an audit of a Next.js application in 2026. The same route can be fully indexed by Google and invisible to the systems that increasingly intercept the query before it reaches a SERP. Teams investing in where AEO and SEO overlap should treat server-rendered HTML as a hard requirement, not a performance preference.

JavaScript File Fetch Rate by AI Crawler
JavaScript File Fetch Rate by AI CrawlerClaudeBot (fetched, did not execute): 23.8; GPTBot (fetched, did not execute): 11.5; Googlebot (fetched and executed): 1001Googlebot (fetched and executed)1002ClaudeBot (fetched, did notexecute)23.83GPTBot (fetched, did not execute)11.5
Ranking the AI crawlers by how often they fetched JS at all — none of them executed it. Source: Vercel and MERJ analysis, 2024

Metadata That Resolves After the Head Closes

The App Router gives you one correct place to declare per-route metadata. Next.js documents a Metadata API where you export either a static metadata object or an async generateMetadata function from layout.js or page.js, and the framework assembles the head tags for you. Simple on paper. Three failure modes in practice.

First, a Client Component cannot export metadata. Next.js supports metadata and generateMetadata only in Server Components. Add a 'use client' directive to a page that was quietly carrying its SEO fields and the export is ignored. Titles collapse to the layout default and canonicals disappear.

Second, streaming metadata in Next.js 15.2 and later changed the contract. The framework now streams the body before generateMetadata resolves for ordinary visitors and holds the response for user agents it classifies as bots. If your generateMetadata call hits a slow upstream — a CMS, a product API, a translation service — browsers see your page fast while every request from a crawler on the htmlLimitedBots list waits for the full head. The symptom is uneven: fast Core Web Vitals, long bot response times, and social previews that occasionally miss.

Third, generateMetadata is called with its own set of params and does not share state with the page render. If your page function reaches into a cache that generateMetadata does not, you get a canonical that disagrees with the rendered content. That is a prime source of content cannibalization, and the cleanup looks a lot like a normal content consolidation project rather than an infrastructure fix.

Pressure test: ask your team to show you three generated head sections from the production build, one each for a static route, a dynamic route, and a route gated behind authentication. If any of them produce the metadata only after hydration, you have an indexation bug the crawler will not report.

ISR Cache Poisoning and the Stale Canonical Problem

Incremental Static Regeneration is the feature teams love and audit least. A page is built once, cached at the edge, and revalidated on a timer or on demand. When the generateMetadata function runs against stale data — a product slug that moved, a URL that was consolidated, a locale alternate that was removed — the cached head is wrong until the next regeneration. The rendered body can be correct and the canonical still point somewhere else.

The failure compounds with on-demand revalidation. A webhook fires, one node regenerates, others serve the previous version until their TTL expires. For a few minutes or a few hours, the same URL returns two different canonical tags depending on which edge node answers. Googlebot samples both. The conflicting signals delay indexing of the new page and, in the worst case, deindex the one that moved.

Audit the revalidation path, not just the output. You want to see the revalidate interval for every route segment, the on-demand triggers, and the fallback behavior when regeneration fails. Fallbacks that serve a 200 with partial data are worse than a 503, because Google indexes the broken version. If a migration is on the roadmap, factor cache invalidation into the plan the same way you would in the SEO migration cost work before any URLs change.

Middleware Redirects and the User-Agent Fork

Next.js middleware runs at the edge before a route is resolved. It is the right place for auth, locale detection, and A/B routing, and the wrong place for anything that depends on knowing whether the request is a crawler. Teams often branch on User-Agent to serve different content, different locales, or different cache keys to bots. That is cloaking if the content differs materially, and it is a maintenance burden either way.

Two specific patterns to flag. A redirect chain that compresses for browsers but expands for crawlers, because an intermediate hop is skipped on known bot UAs. And a locale middleware that 302s a request to a geographic default when it cannot identify the user, which Googlebot will follow and index as the destination. If your root URL silently 302s to /en-us for an unauthenticated crawl, that is the canonical you are telling Google to index.

The fix is to decide redirect intent at the route level, not the middleware, and to prefer 308s for anything permanent. The guide to 301 redirects is the shortest version of why, and the logic applies the same to 308s in a modern stack. While you are in there, pull the full redirect chain for every top organic landing page; the 404 error audit frame is the same one that catches a chain which has gradually grown a hop.

What to Actually Run on a Next.js Audit

A crawler alone will not find most of the above. The audit is a sequence of specific checks, run on production, with the output recorded as diffs.

One more that does not fit the sequence. Google documented that when it encounters a noindex tag it may skip rendering entirely, which means a JavaScript trick that removes a noindex meta tag at runtime will not work. If your dev team has ever shipped a "we noindex it and then fix it in the client" pattern, replace it before the audit, not after.

Diagnostic Sequence for a JavaScript-Rendered Route
  1. 11. Run URL Inspection or Rich Results TestTest how Google crawls and renders the production URL
  2. 22. Review loaded resourcesConfirm no required JS, CSS, or API is blocked or failing
  3. 33. Read JavaScript console outputCapture exceptions WRS hit during render
  4. 44. Inspect the rendered DOMDiff it against the raw HTML for metadata, canonicals, links
  5. 55. Verify HTTP status codesCatch soft 404s where the server returns 200 for missing routes
  6. 66. Check crawl stats in Search ConsoleMonitor ongoing Googlebot and WRS activity on the site
The order Google's own troubleshooting guide prescribes — run it per template, not per domain. Source: Google Search Central, 2025

Core Web Vitals, Streaming SSR, and the LCP Trap

Streaming SSR lets you flush the shell early and fill content as suspended boundaries resolve. It is good for perceived performance and dangerous for Largest Contentful Paint when your hero image, H1, or primary copy sits behind a Suspense boundary. A good LCP score is 2.5 seconds or less at the 75th percentile, segmented by mobile and desktop. A streaming layout that delivers the nav and footer first and the main content second can pass every other metric and still miss LCP.

Audit which element Chrome actually picks as LCP on your top templates, and where it sits in the streaming order. If the LCP element is inside a Suspense boundary waiting on a server fetch, the fix is to hoist that fetch out of the boundary or to preload the asset. Do not optimize for the render waterfall your dev team sees in Chrome DevTools on a fast laptop; optimize for the field data in Search Console and CrUX.

The Trade-Off

Running the audit this way is slower and more expensive than pointing a crawler at the domain. You will spend engineering time diffing head sections across environments, auditing revalidation paths, and arguing about middleware that works fine for users. In exchange, you find the indexation problems that a standard audit misses — the ones that cost you position on bottom-of-funnel queries and leave you out of the answer when ChatGPT and Claude summarize your category. That is almost always worth the week it takes.

Timothy Carter
// written by
Timothy Carter
Industry veteran Timothy Carter is SEO.co’s Chief Revenue Officer. Tim leads all revenue for the company and oversees all customer-facing teams for SEO (search engine optimization) services - including sales, marketing & customer success. He has spent more than 20 years in the world of SEO & Digital Marketing, assisting in everything from SEO for lawyers to complex technical SEO for Fortune 500 clients like Wiley, Box.com, Qualtrics and HP. Tim holds expertise in building and scaling sales operations, helping companies increase revenue efficiency and drive growth from websites and sales teams. When he's not working, Tim enjoys playing a few rounds of disc golf, running, and spending time with his wife and family on the beach...preferably in Hawaii. Over the years he's written for publications like Forbes, Entrepreneur, Marketing Land, Search Engine Journal, ReadWrite and other highly respected online publications. Connect with Tim on Linkedin & Twitter.