How to make your Next.js site appear in ChatGPT (and any LLM) A developer's guide explains how to configure Next.js sites for Generative Engine Optimization (GEO), distinguishing between training crawlers like GPTBot and search bots like OAI-SearchBot. The article details that blocking GPTBot does not prevent ChatGPT Search citations, and provides a robots.txt policy to allow search bots while disallowing training bots. You blocked GPTBot in robots.txt to keep your content out of training runs — and then wondered why ChatGPT Search never cites your docs. Those are different systems . OpenAI’s own crawler docs say each bot is independent: allowing OAI-SearchBot keeps you eligible for ChatGPT search answers while disallowing GPTBot opts you out of foundation-model training. This article is a Next.js App Router playbook for Generative Engine Optimization GEO : how answer engines discover pages, which user-agents actually matter, how to configure robots.ts and sitemaps, how to keep HTML crawlable, and what llms.txt does — and does not — guarantee. Treat “appearing in an LLM” as three separate pipelines: | Pipeline | What it does | Typical bots / tokens | |---|---|---| Training crawl | Collects public pages that may enter future model training | GPTBot , ClaudeBot , Google-Extended token , Common Crawl’s CCBot | Search / answer index | Builds or refreshes retrieval so answers can cite your URLs | OAI-SearchBot , Claude-SearchBot , PerplexityBot , classic Googlebot / Bingbot and partners | User-triggered fetch | Downloads a specific URL because a human asked for it or pasted a link | ChatGPT-User , Claude-User , Perplexity-User | Blocking the training bot does not automatically block the search bot. OpenAI states this explicitly for GPTBot vs OAI-SearchBot . Anthropic documents the same split for ClaudeBot , Claude-SearchBot , and Claude-User . Perplexity documents PerplexityBot for search indexing and Perplexity-User for live fetches. ChatGPT Search can also partner with third-party search providers. OpenAI’s help center documents that rewritten queries may be sent to partners such as Bing and others listed in that article . Independently, OpenAI recommends allowing OAI-SearchBot if you want to appear in ChatGPT search answers. Practical implication: keep search bots allowed From OpenAI’s crawler overview https://developers.openai.com/api/docs/bots : Used to surface websites in ChatGPT’s search features. Sites opted out of OAI-SearchBot will not be shown in ChatGPT search answers , though they can still appear as plain navigational links. OpenAI recommends allowing it in robots.txt and permitting its published IP ranges https://openai.com/searchbot.json . Changes can take about 24 hours to propagate. Crawls content that may be used to train generative foundation models. Disallowing GPTBot signals that content should not be used for that training. It does not control ChatGPT Search eligibility. Used when ChatGPT or Custom GPTs fetch a page because of a user action. OpenAI notes it is not used for automatic web crawling, not used to decide Search inclusion, and that robots.txt rules may not apply because the fetch is user-initiated. Manage Search with OAI-SearchBot ; treat ChatGPT-User as a separate live-fetch channel. Only visits pages submitted as ads on ChatGPT; not used to train foundation models. Relevant if you run ChatGPT ads — ignore it for organic GEO. A common, defensible policy for content sites that want citations but not training: User-agent: OAI-SearchBot Allow: / User-agent: GPTBot Disallow: / Anthropic’s help center https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler updated April 2026 defines three bots: ClaudeBot Claude-SearchBot Claude-User Anthropic honors robots.txt including non-standard Crawl-delay and warns that IP-blocking alone is unreliable because it can prevent the bot from reading your robots.txt . Perplexity’s crawler docs https://docs.perplexity.ai/docs/resources/perplexity-crawlers recommend allowing PerplexityBot so your site can appear in Perplexity search results. Perplexity-User perplexitybot.json / perplexity-user.json — robots.txt alone is not enough when the edge drops the request. Google-Extended https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers is a Important trade-off: for OpenAI you can allow search and disallow training separately. For Google, Google-Extended covers both Gemini training and Gemini grounding. Allow it if you want Gemini apps to ground on your content; disallow it if you want to opt out of those Gemini uses Search itself stays separate via Googlebot . app/robots.ts App Router can generate /robots.txt from a typed file. Official docs: robots.txt file convention https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots . Example that keeps search/citation bots allowed, optionally opts out of training , fences private routes, and advertises the sitemap: python // app/robots.ts import type { MetadataRoute } from 'next' const SITE = 'https://example.com' export default function robots : MetadataRoute.Robots { return { rules: { userAgent: ' ', allow: '/', disallow: '/api/', '/admin/', '/drafts/' , }, // ChatGPT Search + live fetch { userAgent: 'OAI-SearchBot', allow: '/' }, { userAgent: 'ChatGPT-User', allow: '/' }, // Training opt-out optional — remove this rule to allow training { userAgent: 'GPTBot', disallow: '/' }, // Claude { userAgent: 'Claude-SearchBot', allow: '/' }, { userAgent: 'Claude-User', allow: '/' }, { userAgent: 'ClaudeBot', disallow: '/' }, // Perplexity { userAgent: 'PerplexityBot', allow: '/' }, { userAgent: 'Perplexity-User', allow: '/' }, // Gemini grounding/training token allow if you want Gemini apps to use you { userAgent: 'Google-Extended', allow: '/' }, , sitemap: ${SITE}/sitemap.xml , host: SITE, } } After deploy, open https://your-domain/robots.txt and confirm the groups look right. Spoofed user-agents exist — for verification, OpenAI and Perplexity publish IP range JSON files; Anthropic currently points publishers at robots.txt rather than relying on IP blocks. Crawlers need URLs to discover. Next.js can generate /sitemap.xml from app/sitemap.ts docs https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap : python // app/sitemap.ts import type { MetadataRoute } from 'next' export default async function sitemap : Promise