Next.js SEO: Why Your SaaS Website Isn't Getting Indexed (And How to Fix It)

We've audited over 70 funded SaaS websites. More than half are built on Next.js. And a shocking number of them — companies that raised $5M, $20M, even $60M — have basic Next.js SEO errors that make Google treat their pages as blank. Here's every mistake we've found, and exactly how to fix each one.

The Next.js SEO Myth

There's a common belief among SaaS founders: "We're using Next.js, so our SEO is handled." It's not. Next.js is an excellent framework with first-class SEO capabilities — but those capabilities require correct configuration. A Next.js app with default settings, or with common misconfigurations, can be nearly invisible to Google.

The framework doesn't guarantee indexing. It gives you the tools. You have to use them correctly.

58%
of SaaS sites we audited use Next.js or React
43%
had at least one critical JS SEO issue
21%
had pages Google couldn't index at all

The 7 Most Common Next.js SEO Mistakes

🔴 Issue #1 — Critical

BAILOUT_TO_CLIENT_SIDE_RENDERING

This is the most damaging Next.js SEO error. When a server component can't render fully on the server, Next.js falls back to pure client-side rendering. Googlebot sees an empty HTML shell with no content — just a loading spinner or blank div.

How it happens:

✅ The Fix

Check your browser console and server logs for BAILOUT_TO_CLIENT_SIDE_RENDERING. For each occurrence:

// Move browser-dependent code to a Client Component
'use client'

import { useState, useEffect } from 'react'
import SomeBrowserLibrary from 'some-browser-library'

export function ClientOnlySection() {
  const [data, setData] = useState(null)
  useEffect(() => {
    // Browser code here — safe
  }, [])
  return <div>{data}</div>
}

Wrap useSearchParams() in a Suspense boundary:

import { Suspense } from 'react'

export default function Page() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <SearchParamsComponent />
    </Suspense>
  )
}
🔴 Issue #2 — Critical

Missing or Wrong Canonical Tags

Canonical tags tell Google which URL is the "original" when the same content exists at multiple URLs. In Next.js, canonical issues show up as: empty canonical (<link rel="canonical" href="">), wrong canonical (pointing to a staging URL, localhost, or another page), or no canonical at all.

We've seen production SaaS sites where every page had canonical pointing to http://localhost:3000/ — meaning Google thought the entire site was duplicating localhost content. Every page was marked for de-indexing.

✅ The Fix (App Router)
// app/page.tsx
export const metadata = {
  alternates: {
    canonical: 'https://yourdomain.com/',
  },
}

// For dynamic routes: app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
  return {
    alternates: {
      canonical: `https://yourdomain.com/blog/${params.slug}`,
    },
  }
}
✅ The Fix (Pages Router)
import Head from 'next/head'

export default function BlogPost({ post }) {
  return (
    <>
      <Head>
        <link rel="canonical" href={`https://yourdomain.com/blog/${post.slug}`} />
      </Head>
      {/* content */}
    </>
  )
}
🟠 Issue #3 — High Impact

Multiple H1 Tags

Google uses the H1 tag to understand what a page is about. Multiple H1s send conflicting signals. In Next.js, this happens when layout files (like app/layout.tsx) include an H1 that stacks with the page's own H1 — or when reusable Hero/Banner components each render an H1.

We found one funded SaaS startup with 5 H1 tags on their homepage — every major section had its own H1.

✅ The Fix

Audit first:

// Run in browser console
document.querySelectorAll('h1').length // should return 1

Then fix: layouts should never contain H1 tags. Only pages should. In reusable components, use H2 for section headings:

// ❌ Wrong — layout with H1
export default function Layout({ children }) {
  return <div><h1>AutoSEOBot</h1>{children}</div>
}

// ✅ Right — layout without H1
export default function Layout({ children }) {
  return <div><nav>...</nav>{children}</div>
}
🟠 Issue #4 — High Impact

Missing Metadata (Title, Description, OG Tags)

A default Next.js app has no <title>, no <meta description>, and no Open Graph tags unless you add them. Many teams add metadata to the homepage but forget product pages, blog posts, or documentation.

✅ The Fix (App Router — recommended)
// app/layout.tsx — default metadata (fallback for all pages)
export const metadata = {
  title: {
    default: 'Your SaaS — Product Tagline',
    template: '%s | Your SaaS',
  },
  description: 'Your default meta description here.',
  openGraph: {
    siteName: 'Your SaaS',
    images: [{ url: 'https://yourdomain.com/og-image.png', width: 1200, height: 630 }],
  },
}

// app/features/page.tsx — page-level override
export const metadata = {
  title: 'Features',
  description: 'Everything Your SaaS can do...',
  alternates: { canonical: 'https://yourdomain.com/features' },
}
🟠 Issue #5 — High Impact

No Sitemap or a Broken Sitemap

Sitemaps tell Google what pages exist. In Next.js, a common mistake is either forgetting to create one, or having a sitemap that returns a 404 or 500 error. We've seen funded startups where /sitemap.xml returns a 404 — Google has no map of the site.

✅ The Fix

In Next.js 13+ App Router, add app/sitemap.ts:

import { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    { url: 'https://yourdomain.com', lastModified: new Date(), changeFrequency: 'weekly', priority: 1 },
    { url: 'https://yourdomain.com/features', lastModified: new Date(), changeFrequency: 'monthly', priority: 0.8 },
    { url: 'https://yourdomain.com/pricing', lastModified: new Date(), changeFrequency: 'monthly', priority: 0.8 },
    // Add all public pages
  ]
}

Then submit https://yourdomain.com/sitemap.xml in Google Search Console.

🟡 Issue #6 — Medium Impact

Misconfigured robots.txt Blocking Googlebot

A single wrong line in robots.txt can make your entire site invisible to Google. The most dangerous pattern:

User-agent: *
Disallow: /

This blocks all crawlers from everything. We've seen live production SaaS sites with this exact configuration — usually copied from a .env.example or staging robots.txt that never got updated for production.

✅ The Fix
// app/robots.ts
import { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: ['/api/', '/admin/', '/dashboard/'],
    },
    sitemap: 'https://yourdomain.com/sitemap.xml',
  }
}
🟡 Issue #7 — Medium Impact

No Structured Data (Schema Markup)

Schema markup helps Google understand what your page is about and can unlock rich results (FAQ dropdowns, star ratings, breadcrumbs). Most Next.js SaaS sites have zero schema markup.

✅ The Fix
// Add to any Next.js page
export default function PricingPage() {
  const schema = {
    "@context": "https://schema.org",
    "@type": "SoftwareApplication",
    "name": "Your SaaS",
    "applicationCategory": "BusinessApplication",
    "offers": {
      "@type": "Offer",
      "price": "49",
      "priceCurrency": "USD"
    }
  }

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
      />
      {/* page content */}
    </>
  )
}

The Next.js SEO Audit Checklist

Before deploying any Next.js site, run through this checklist:

Pro tip: Use curl -s https://yourdomain.com/your-page | grep -i "<title\|description\|canonical\|h1" to quickly verify server-side rendered HTML. If your title, description, canonical, and H1 don't appear in the curl output — Google can't see them either.

How to Verify Google Can Actually Index Your Next.js Pages

  1. Google Search Console → URL Inspection — Paste any URL and check "Coverage" status. "URL is on Google" = indexed. "Crawled — currently not indexed" or "Discovered — currently not indexed" = problem.
  2. curl test — Run curl -s https://yourdomain.com | grep -i "title\|description". If your page title doesn't appear in the raw HTML response, Googlebot can't see it.
  3. Rich Results Test — Google's tool at search.google.com/test/rich-results shows exactly what structured data Google finds.
  4. Mobile Usability — Google uses mobile-first indexing. Check search.google.com/search-console/mobile-usability for issues.

Next.js App Router vs Pages Router: SEO Differences

If you're still on the Pages Router, you can still implement good SEO — but App Router makes it significantly easier:

If you're building a new SaaS site, use App Router. If you're on Pages Router and SEO is suffering, a migration to App Router is worth planning for.

When to Bring In Professional Help

If you've fixed the technical issues above and you're still not ranking, the problem shifts from technical to strategic: the right keywords, content depth, backlink authority, and competitive positioning. That's where an SEO audit from a team that understands both SaaS and Next.js pays off.

The signals that suggest you need professional help:

See also: Technical SEO Checklist for SaaS · Why Your SaaS Website Isn't Ranking · Fix Noindex & Canonical Issues

Get a Free Next.js SEO Audit

We'll audit your Next.js site for every issue on this list — plus content gaps, backlink opportunities, and GEO readiness. Free, delivered in 48 hours.

Get Your Free Audit →

Frequently Asked Questions

Does Next.js automatically handle SEO?
Not automatically. Next.js provides the tools (server-side rendering, generateMetadata, canonical tags) but you have to configure them correctly. A default Next.js app without proper metadata, canonical tags, and SSR configuration will rank poorly — or not at all.
Why is Googlebot not indexing my Next.js site?
The most common reasons: (1) BAILOUT_TO_CLIENT_SIDE_RENDERING — your pages are falling back to pure client-side rendering, so Googlebot sees an empty shell. (2) Missing or wrong canonical tags. (3) A robots.txt that accidentally blocks Googlebot. (4) Dynamic routes with no static generation or server-rendering. Check Google Search Console for "Crawled — currently not indexed" and "Discovered — currently not indexed" errors.
What is BAILOUT_TO_CLIENT_SIDE_RENDERING in Next.js?
It's a Next.js warning that appears when a server component can't fully render on the server and falls back to the browser. Common triggers include using browser-only APIs (window, document) in server components, hooks in server components, or importing client-only libraries without the 'use client' directive. When this happens, Googlebot — which doesn't execute JavaScript deeply — sees a blank page.
How do I add canonical tags in Next.js App Router?
In Next.js App Router, use generateMetadata or a static metadata export in each page.tsx: export const metadata = { alternates: { canonical: 'https://yourdomain.com/your-page' } }. For the Pages Router, use next/head: <link rel='canonical' href='...' />. Make sure every page has a unique canonical pointing to itself — duplicate or missing canonicals cause index dilution.
Can I fix multiple H1 tags in a Next.js site?
Yes. Multiple H1s usually happen when reusable components (Hero, Banner, Section) each render an H1, or when a layout file adds an H1 that stacks with the page's own H1. Audit with: document.querySelectorAll('h1').length in browser console. Fix by ensuring only one H1 per page — usually the main headline. Use H2–H6 for subheadings.
Does using an SEO agency help if my site is built on Next.js?
Yes — but only if the agency understands JavaScript SEO. Many traditional agencies treat Next.js like a static site and miss framework-specific issues like hydration errors, streaming SSR edge cases, and Suspense boundary render bailouts. Look for agencies that do technical audits with a developer lens, not just keyword reports.