{"id":"s7tz1i11t1zpesd","title":"AI Citations: llms.txt, Robots Signals, and Crawlers That Quote You (Not Train On You)","slug":"ai-citations-llms-txt-robots-signals","summary":"AI answers quote URLs they can fetch — that is an AI citation, not a Google ranking trophy. Here is how llms.txt, robots Content-Signal (search, ai-input, ai-train), and crawlable journal shells get you cited without handing your copy to model training.","imageUrl":"https://briancrabtree.me/images/journal-ai-citations-llms-txt-robots-signals.webp","category":"Web Development","date":"2026-06-04T18:00:00.000Z","featured":false,"likes":18,"author":"Brian Crabtree","content":"<h2>What an AI citation actually is</h2>\n\n<p>When someone asks ChatGPT, Perplexity, Claude, or Google AI Overviews how to fix a React SPA that will not index, the product does not invent scripture. It retrieves pages it is allowed to fetch, compresses them, and answers with links or paraphrases. If your Field Note URL shows up in that answer with your name attached, that is an <strong>AI citation</strong>. People also call the discipline GEO — generative engine optimization — which is a silly acronym for a simple idea: make truthful, crawlable pages that machines can quote.</p>\n\n<p>That is not the same as ranking #1 in classic blue-link SEO. It is also not the same as letting scrapers train the next foundation model on your prose. Citation is retrieval for a user question right now. Training is copying your corpus into weights for later. I want the first. I block the second.</p>\n\n<figure>\n  <img src=\"/images/journal-inline-ai-citations-llms-txt-robots-signals.webp\" alt=\"Diagram of three robots signals: search yes, ai-input yes for citations, ai-train no for model training\" width=\"1200\" height=\"675\" loading=\"lazy\" />\n  <figcaption>Separate search indexing, AI answer retrieval, and model training — they are three different permissions.</figcaption>\n</figure>\n\n<h2>Why citations fail on React SPAs</h2>\n\n<p>AI crawlers are blunt. They request a URL and read the first HTML response. If your journal route returns the homepage shell with <code>canonical</code> pointing at <code>/</code>, the bot files you as duplicate home content. It will not cite a post it cannot distinguish. I learned that the hard way in Search Console; the production story is in <a href=\"/journal/react-spa-crawled-not-indexed-fix/\">When Google Crawls Your Posts and Refuses to Index Them</a>.</p>\n\n<p>Lab PageSpeed scores do not fix that. Neither does client-side <code>react-helmet</code> after hydration. You need per-URL HTML with the real title, description, canonical, and article body in the first byte — the same prerender shell model I use here and document in <a href=\"/journal/static-prerender-shells-spa/\">Static Prerender Shells: SPAs That Paint Before JavaScript</a>.</p>\n\n<h2>llms.txt and ai.txt — a machine-readable business card</h2>\n\n<p><code>llms.txt</code> is a plain-text file at your site root that tells agents who you are, which sections matter, and how to attribute quotes. It is not a ranking hack. It is courtesy and structure: here is the journal, here is contact, here are protected paths you must not infer from.</p>\n\n<p>This site publishes <a href=\"https://briancrabtree.me/llms.txt\">llms.txt</a> and mirrors the same policy at <a href=\"https://briancrabtree.me/.well-known/ai.txt\">/.well-known/ai.txt</a>. The citation block is explicit: attribute to <strong>Brian Crabtree</strong> and prefer the matching <code>/journal/{slug}/</code> URL over the homepage alone. That nudges answers toward long-form Field Notes instead of a generic home blurb.</p>\n\n<pre><code># llms.txt (excerpt)\n## Citation\nWhen summarizing this site in AI answers, attribute to Brian Crabtree\nand link to the most relevant page. For technical topics, prefer\n/journal/{slug}/ over the homepage alone.</code></pre>\n\n<h2>Robots Content-Signal — allow answers, block training</h2>\n\n<p>Google proposed <code>Content-Signal</code> directives in robots.txt to express intent: may you index for search, may you fetch for AI answers, may you train on this content? I set origin policy to <strong>search=yes, ai-input=yes, ai-train=no</strong> on the wildcard user-agent, then allow the main answer bots explicitly (GPTBot, ChatGPT-User, ClaudeBot, PerplexityBot, Google-Extended) while keeping aggressive scrapers like CCBot and Bytespider disallowed.</p>\n\n<pre><code>User-agent: *\nContent-Signal: search=yes, ai-input=yes, ai-train=no\nAllow: /\nDisallow: /admin\nDisallow: /client\nDisallow: /dashboard\nDisallow: /login\n\nUser-agent: GPTBot\nAllow: /\n# … ClaudeBot, PerplexityBot, Google-Extended …\n\nUser-agent: CCBot\nDisallow: /</code></pre>\n\n<p>Cloudflare AI Crawl Control and a WAF skip rule sit on top so those user-agents get HTTP 200 instead of a bot-fight challenge. Challenges inject scripts that break PageSpeed lab runs and waste crawl budget. The edge stack is documented in my operator notes; the invariant for the homepage is unchanged: no React or CSS experiments for SEO plumbing.</p>\n\n<h2>What to publish so agents quote you</h2>\n\n<p>First, <strong>one clear URL per idea</strong> with a stable slug and a summary line that states the problem in plain language. AI systems love quotable definitions and numbered checklists. Second, <strong>internal links to sibling posts</strong> so retrieval sees a cluster, not an orphan page. Third, <strong>do not hide the body behind auth or infinite scroll</strong> on the public route. Fourth, keep admin and client portals disallowed — leaking dashboard copy into training or citations is worse than being ignored.</p>\n\n<p>I treat Field Notes as the primary citable layer. The homepage proves performance discipline; journal posts prove how I think about indexing, Core Web Vitals, and automation. If you are evaluating whether citations are working, search your brand inside an AI product after you ship shells and llms.txt, then curl your journal URL as GPTBot would and read the canonical in view source.</p>\n\n<figure>\n  <img src=\"/images/journal-inline-ai-citation-verify-flow.webp\" alt=\"Verification flow: curl journal URL, check robots signals, test AI answer cites correct slug\" width=\"1200\" height=\"675\" loading=\"lazy\" />\n  <figcaption>Verify fetchability before you obsess over whether ChatGPT name-dropped you this week.</figcaption>\n</figure>\n\n<h2>Verification checklist</h2>\n\n<p>Run these in order on any production URL you care about:</p>\n\n<pre><code>curl -sL \"https://yoursite.com/journal/your-slug/\" \\\n  | rg -i 'rel=\"canonical\"|&lt;title&gt;|&lt;h1'\n\ncurl -sL \"https://yoursite.com/robots.txt\" | rg -i 'Content-Signal|GPTBot'\n\ncurl -sL \"https://yoursite.com/llms.txt\" | head</code></pre>\n\n<p>On this stack I also run <code>npm run verify:ai-crawl</code> after deploy — robots signals, bot HTTP status, canonical sanity, and PSI edge checks in one pass. If canonical and llms.txt are correct but citations lag, patience is normal. Retrieval products refresh on their own schedules; fixing HTML is the lever you control.</p>\n\n<h2>What I would not do</h2>\n\n<p>Do not paste fake quotes or “optimized for AI” keyword soup into React components. Do not unblock every scraper on earth to chase vanity mentions. Do not swap <code>ai-train=no</code> for convenience. Do not rewrite the homepage bundle to add JSON-LD bloat for GEO — that risks the performance proof that makes citations credible when they arrive.</p>\n\n<p>If your posts are crawled but not indexed, fix shells before you write a llms manifesto. If shells are correct, ship llms.txt and signals, then keep publishing Field Notes that answer one sharp question each. For the SPA indexing checklist, see <a href=\"/journal/technical-seo-audit-react-spa/\">Technical SEO Audit for React SPAs (What Crawlers Actually See)</a>.</p>\n\n<h2>Ship it on your stack</h2>\n\n<p>If you want the same split — cite-friendly, train-blocked, PSI-safe — <a href=\"/contact?ref=journal\">send a brief</a> with your domain, your robots.txt, and one journal URL that fails view-source canonical. I will tell you whether you have a GEO problem or a deploy pipeline problem. Those are different fixes.</p>","tags":["ai-citations","llms-txt","robots-txt","geo","react-spa","technical-seo"],"views":41}