Next.js SEO: how do I get my site cited by ChatGPT?

Make every page readable without JavaScript, describe it, and let ChatGPT's search crawler in. In Next.js that means Server Components with the text in the HTML, the Metadata API for title, canonical and languages, app/robots.ts allowing OAI-SearchBot, app/sitemap.ts, and JSON-LD in the page. None of this guarantees a citation; it removes the reasons ChatGPT cannot use your page.

Facts

  • OpenAI says sites opted out of OAI-SearchBot will not be shown in ChatGPT search answers, and a robots.txt change can take about 24 hours to take effect. (OpenAI: Overview of OpenAI crawlers)
  • OpenAI says disallowing GPTBot only signals that content should not be used to train its models; ChatGPT-User visits are started by users and are not used to decide what appears in search. (OpenAI: Overview of OpenAI crawlers)
  • Next.js says the metadata export and generateMetadata, available since v13.2.0, work only in Server Components so metadata can be in the initial HTML. (Next.js docs: generateMetadata)
  • Next.js has generated robots.txt from app/robots.ts since v13.3.0, with rules per user agent. (Next.js docs: robots.txt)
  • app/sitemap.ts supports alternate language URLs since Next.js v14.2.0, and Google's limit is 50,000 URLs per sitemap. (Next.js docs: sitemap.xml)

Which OpenAI crawler do I need to allow?

OAI-SearchBot is the one that decides whether ChatGPT search can show your site. The three have different jobs, so block or allow them separately.

// app/robots.ts
import type { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      { userAgent: "*", allow: "/" },
      // ChatGPT search: must be allowed to be shown in answers
      { userAgent: "OAI-SearchBot", allow: "/" },
      // Model training: your choice, it does not decide search
      { userAgent: "GPTBot", allow: "/" },
    ],
    sitemap: "https://www.example.com/sitemap.xml",
  };
}
User agentWhat it doesEffect of blocking it
OAI-SearchBotCrawls sites for ChatGPT's search featuresYour pages are not shown in ChatGPT search answers
GPTBotCrawls content that may train OpenAI's modelsContent is not used for training; search is not affected
ChatGPT-UserVisits a page when a user's question needs itrobots.txt may not apply; it does not decide search

Why must the text be server-rendered?

Because ChatGPT's crawlers read the HTML your server sends and do not run JavaScript. In the App Router, pages are Server Components by default; text fetched in a Client Component after load is missing from that HTML. Keep the page a Server Component and move only the interactive parts to "use client" files.

What should each page's metadata include?

A unique title and description, a canonical URL and, on multilingual sites, the language alternates. generateMetadata builds them per page; Next.js turns alternates into rel="canonical" and hreflang links. JSON-LD goes in the page as a plain script tag, with "<" escaped as the Next.js docs show, and must say the same as the visible text.

// app/guides/[slug]/page.tsx (a Server Component: no "use client")
import type { Metadata } from "next";

export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
  const { slug } = await params;
  const page = await getPage(slug);
  return {
    title: page.title,
    description: page.description,
    alternates: {
      canonical: `/guides/${slug}`,
      languages: { "pt-BR": `/pt/guides/${slug}`, es: `/es/guides/${slug}` },
    },
  };
}

export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const page = await getPage(slug);
  const jsonLd = { "@context": "https://schema.org", "@type": "Article", headline: page.title };
  return (
    <article>
      <script type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c") }} />
      <h1>{page.title}</h1>
      <p>{page.answer}</p>
    </article>
  );
}

How do I test it like a crawler?

Fetch the live page without a browser and look for the answer text, the title, the canonical and the JSON-LD. If the text is not in the raw HTML, ChatGPT cannot quote it.

# 1. The text is in the raw HTML (no JavaScript run)
curl -s -A "OAI-SearchBot" https://www.example.com/guides/my-page | grep -i "a sentence from the answer"

# 2. Title, canonical and JSON-LD are there too
curl -s https://www.example.com/guides/my-page | grep -iE "<title>|rel=\"canonical\"|ld\+json"

# 3. robots.txt and sitemap answer
curl -s https://www.example.com/robots.txt
curl -s https://www.example.com/sitemap.xml | grep -c "<loc>"

Does a perfect setup get me cited?

No. The setup makes your pages readable and eligible; ChatGPT picks sources by how well a page answers the question. The content still has to be the clearest answer to what your buyers ask.

Step by step

  1. Check robots. Add app/robots.ts allowing OAI-SearchBot and pointing to the sitemap; decide GPTBot separately.
  2. Keep pages on the server. Make sure every public page is a Server Component with its main text in the HTML.
  3. Add metadata. Use generateMetadata for a unique title, description, canonical and language alternates on every page.
  4. Add JSON-LD and the sitemap. Render JSON-LD in the page and list every public URL in app/sitemap.ts.
  5. Test the live site. Run the curl checks after the deploy, not on localhost.

Common mistakes

  • Blocking GPTBot and OAI-SearchBot together with one rule when only training was meant to be blocked.
  • Loading the article text in useEffect, so the HTML a crawler gets is empty.
  • Copying the same title, description or canonical to every page from the root layout.
  • JSON-LD that claims things the visible page does not say.

How easyIndex solves it

easyIndex is a good fit for a Next.js site because the work stays in your repo: your coding agent writes the pages and easyIndex gives the steps over MCP, audits each page when submitted and checks it on the live site, so a step completes on evidence it checks, not on the agent's report.

Then the dashboard shows AI-assistant visitors per page and which pages AI crawlers read. One plan with every feature: US$ 30 per website a month, 7-day free trial with one website.

Frequently asked questions

Should I block GPTBot?

It is your choice. OpenAI says GPTBot is for training and OAI-SearchBot for search, so blocking GPTBot alone does not remove you from ChatGPT search.

Do I need the Pages Router or App Router?

Either can send server-rendered HTML. This guide uses the App Router, where pages are Server Components by default and the Metadata API lives.

Is llms.txt required?

No. ChatGPT search reads your pages; an llms.txt file does not replace text in the HTML.

How long until a robots.txt change counts?

OpenAI says about 24 hours for its search systems to adjust after a robots.txt update.

Continue reading

Last verified on .

Next.js SEO: how to get your site cited by ChatGPT