<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Osama Mumtaz</title>
    <description>The latest articles on DEV Community by Osama Mumtaz (@osama1malik).</description>
    <link>https://dev.to/osama1malik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F88231%2F8b12b1d7-86ac-44f4-95f0-c9b79e5f690a.png</url>
      <title>DEV Community: Osama Mumtaz</title>
      <link>https://dev.to/osama1malik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/osama1malik"/>
    <language>en</language>
    <item>
      <title>I published an open dataset of every AI crawler user agent (GPTBot, ClaudeBot + 26 more)</title>
      <dc:creator>Osama Mumtaz</dc:creator>
      <pubDate>Sat, 08 Aug 2026 00:12:21 +0000</pubDate>
      <link>https://dev.to/osama1malik/i-published-an-open-dataset-of-every-ai-crawler-user-agent-gptbot-claudebot-26-more-3he4</link>
      <guid>https://dev.to/osama1malik/i-published-an-open-dataset-of-every-ai-crawler-user-agent-gptbot-claudebot-26-more-3he4</guid>
      <description>&lt;p&gt;While building AI-crawler tooling I kept needing the same thing: a current,&lt;br&gt;
machine-readable list of AI crawler user agents. Every blog post has a partial&lt;br&gt;
list, half of them outdated, none of them structured. So I published mine as an&lt;br&gt;
open dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/osamamumtaz01/ai-crawler-user-agents" rel="noopener noreferrer"&gt;https://github.com/osamamumtaz01/ai-crawler-user-agents&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Live endpoint (always current, CORS enabled):&lt;/strong&gt; &lt;a href="https://geoprompttracker.com/data/ai-crawlers.json" rel="noopener noreferrer"&gt;https://geoprompttracker.com/data/ai-crawlers.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;28 crawlers — GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot,&lt;br&gt;
Google-Extended, Bytespider, Applebot-Extended, Meta-ExternalAgent, and the&lt;br&gt;
rest. JSON + CSV, CC BY 4.0. Also on Kaggle and Hugging Face.&lt;/p&gt;
&lt;h2&gt;
  
  
  The field that actually matters: &lt;code&gt;purpose&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The mistake I see constantly: treating "AI bots" as one thing. AI companies run&lt;br&gt;
&lt;strong&gt;separate crawlers for different jobs&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Training crawlers&lt;/strong&gt; (GPTBot, ClaudeBot, Google-Extended) collect content to
train future models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search crawlers&lt;/strong&gt; (OAI-SearchBot, PerplexityBot, Claude-SearchBot) index
pages so assistants can &lt;em&gt;cite&lt;/em&gt; them in live answers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Blocking a training crawler is an opt-out of model training. Blocking a search&lt;br&gt;
crawler removes you from AI answers entirely. Very different decisions — and&lt;br&gt;
because each company uses different bot names for each job, people block the&lt;br&gt;
wrong one all the time.&lt;/p&gt;

&lt;p&gt;So each entry carries a &lt;code&gt;purpose&lt;/code&gt; field (&lt;code&gt;training&lt;/code&gt; / &lt;code&gt;search&lt;/code&gt; / &lt;code&gt;both&lt;/code&gt;), which&lt;br&gt;
makes policy decisions programmable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;crawlers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://geoprompttracker.com/data/ai-crawlers.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// "Don't train on my content, but keep citing me"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crawlers&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;purpose&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;training&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`User-agent: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nDisallow: /`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The judgment call: &lt;code&gt;robotsCompliance&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The other field worth explaining. It rates whether each bot honors robots.txt&lt;br&gt;
&lt;strong&gt;in practice&lt;/strong&gt; — not just what the vendor claims. &lt;code&gt;yes&lt;/code&gt;, &lt;code&gt;partial&lt;/code&gt;, &lt;code&gt;no&lt;/code&gt;, or&lt;br&gt;
&lt;code&gt;unknown&lt;/code&gt;, and every rating comes with a sourced &lt;code&gt;robotsNote&lt;/code&gt; explaining the&lt;br&gt;
reasoning (e.g. Perplexity is rated &lt;code&gt;partial&lt;/code&gt; because independent&lt;br&gt;
investigations observed undeclared fetching despite documented compliance).&lt;/p&gt;

&lt;p&gt;That's also why robots.txt alone isn't enforcement — for bots rated &lt;code&gt;no&lt;/code&gt; or&lt;br&gt;
&lt;code&gt;unknown&lt;/code&gt;, pair it with a WAF/CDN rule if blocking actually matters to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other things you can do with it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Detect AI crawler traffic in middleware or log analysis&lt;/li&gt;
&lt;li&gt;Audit which bots your current robots.txt actually allows&lt;/li&gt;
&lt;li&gt;Research crawler behavior over time (the dataset is reviewed monthly)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you spot a crawler I'm missing or a compliance rating that's gone stale,&lt;br&gt;
issues and sourced corrections are very welcome — especially for the &lt;code&gt;unknown&lt;/code&gt;&lt;br&gt;
entries.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>ai</category>
      <category>seo</category>
    </item>
    <item>
      <title>The 30-Second Raw-HTML Smoke Test for AI Visibility</title>
      <dc:creator>Osama Mumtaz</dc:creator>
      <pubDate>Wed, 29 Jul 2026 04:11:55 +0000</pubDate>
      <link>https://dev.to/osama1malik/the-30-second-raw-html-smoke-test-for-ai-visibility-9o3</link>
      <guid>https://dev.to/osama1malik/the-30-second-raw-html-smoke-test-for-ai-visibility-9o3</guid>
      <description>&lt;p&gt;The 30-Second Raw-HTML Smoke Test for AI Visibility&lt;br&gt;
Your page looks perfect in the browser. Your Lighthouse score is green. Analytics look normal.&lt;/p&gt;

&lt;p&gt;None of that tells you what an AI assistant can actually read on your page.&lt;/p&gt;

&lt;p&gt;This came out of a genuinely good back-and-forth with &lt;a class="mentioned-user" href="https://dev.to/alexshev"&gt;@alexshev&lt;/a&gt; in the comments of my last post, and they framed it better than I had: treat the raw HTML check as a smoke test. It doesn't prove your site is great for AI readers. It just catches the embarrassing failures early — before you spend a month wondering why you never show up in AI answers.&lt;/p&gt;

&lt;p&gt;Why the browser lies to you&lt;br&gt;
When you load a page, your browser executes JavaScript, waits for fetches, hydrates the framework, and renders the final result. What you see is the end state.&lt;/p&gt;

&lt;p&gt;Most AI crawlers don't do that. GPTBot, ClaudeBot, and PerplexityBot generally fetch the raw HTML document and read what's there. No rendering queue, no waiting for your useEffect to resolve. Google is the notable exception — Googlebot renders JavaScript, and AI Overviews build on that index — but assume every other AI system reads the initial response only.&lt;/p&gt;

&lt;p&gt;So there's a gap between what humans see and what machines can reliably extract. And the gap is invisible precisely because everything looks fine.&lt;/p&gt;

&lt;p&gt;The one thing most devs get wrong&lt;br&gt;
DevTools Inspector is not raw HTML.&lt;/p&gt;

&lt;p&gt;The Elements panel shows the live DOM — after JavaScript has run. It will happily show you content that no AI crawler will ever see.&lt;/p&gt;

&lt;p&gt;What you want is View Source (Cmd/Ctrl + U). That's the actual document the server sent.&lt;/p&gt;

&lt;p&gt;If your content is in the Inspector but not in View Source, it's client-rendered. That's the whole failure mode in one sentence.&lt;/p&gt;

&lt;p&gt;The test itself&lt;br&gt;
Pick your poison - all three take under a minute.&lt;/p&gt;

&lt;p&gt;Option 1 — View Source. Cmd/Ctrl + U, then Cmd/Ctrl + F and search for your headline, your price, your address. Not there? That's your answer.&lt;/p&gt;

&lt;p&gt;Option 2 — Disable JavaScript. In Chrome DevTools: Cmd/Ctrl + Shift + P → type "Disable JavaScript" → reload. You're now looking at roughly what a non-rendering crawler gets.&lt;/p&gt;

&lt;p&gt;Option 3 — curl. Fastest for a quick gut check:&lt;/p&gt;

&lt;p&gt;curl -sL &lt;a href="https://yoursite.com" rel="noopener noreferrer"&gt;https://yoursite.com&lt;/a&gt; | grep -i "your headline here"&lt;br&gt;
Want to actually read the text a crawler would extract? This strips tags and gives you the first 2000 characters of real content:&lt;/p&gt;

&lt;p&gt;curl -sL &lt;a href="https://yoursite.com" rel="noopener noreferrer"&gt;https://yoursite.com&lt;/a&gt; | python3 -c "&lt;br&gt;
import sys, re&lt;br&gt;
h = sys.stdin.read()&lt;br&gt;
h = re.sub(r'&amp;lt;(script|style)[^&amp;gt;]&lt;em&gt;&amp;gt;.&lt;/em&gt;?&amp;lt;/\1&amp;gt;', ' ', h, flags=re.S)&lt;br&gt;
print(re.sub(r'\s+', ' ', re.sub(r'&amp;lt;[^&amp;gt;]+&amp;gt;', ' ', h))[:2000])&lt;br&gt;
"&lt;br&gt;
If that output is mostly navigation and boilerplate — or nearly empty — you've found your problem.&lt;/p&gt;

&lt;p&gt;The four failure modes to look for&lt;br&gt;
Alex's list is the useful part here, because it's specific enough to actually run against a page:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Empty HTML. The classic CSR single-page app. Your  is &lt;/p&gt; and everything else arrives via JS. Crawlers get a shell.
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Missing business facts. Title, offer, location, product details. These are the things an AI needs to describe or recommend you. If they're not in the initial response, you're asking the model to guess.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Invisible offers. Pricing loaded from an API on mount. Very common, very costly — "how much does X cost" is exactly the kind of question people ask assistants.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Client-only content. Tabs, accordions, "load more," lazy-mounted sections. Content that exists but only after an interaction the crawler will never perform.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The fix for all four is the same shape: get the critical content into the initial HTML. SSR, SSG, ISR, or prerendering — whichever your stack supports. You don't have to abandon React. Next.js, Nuxt, Remix, SvelteKit all solve this by default; the problem is almost always plain CSR or content deliberately deferred to the client.&lt;/p&gt;

&lt;p&gt;Layer two: structured data as insurance&lt;br&gt;
Here's the nuance I'd add to the smoke test, because passing it isn't quite the finish line.&lt;/p&gt;

&lt;p&gt;Even when your content is in the raw HTML, an agent can still flatten it if the structure is ambiguous. It gets the words but loses the relationships — which number is the price, which line is the address, what's the product versus a related item.&lt;/p&gt;

&lt;p&gt;That's where JSON-LD earns its keep:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "&lt;a class="mentioned-user" href="https://dev.to/context"&gt;@context&lt;/a&gt;": "&lt;a href="https://schema.org" rel="noopener noreferrer"&gt;https://schema.org&lt;/a&gt;",&lt;br&gt;
  "@type": "Product",&lt;br&gt;
  "name": "Acme Widget",&lt;br&gt;
  "offers": {&lt;br&gt;
    "@type": "Offer",&lt;br&gt;
    "price": "49.00",&lt;br&gt;
    "priceCurrency": "USD",&lt;br&gt;
    "availability": "&lt;a href="https://schema.org/InStock" rel="noopener noreferrer"&gt;https://schema.org/InStock&lt;/a&gt;"&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;That's an unambiguous, machine-readable copy of your hard facts, sitting right there in the initial HTML. It survives even a shallow skim.&lt;/p&gt;

&lt;p&gt;So the rule of thumb ends up being two layers:&lt;/p&gt;

&lt;p&gt;Critical content in the initial HTML — so it can be read at all.&lt;br&gt;
Hard facts mirrored in structured data — so they can't be misread.&lt;br&gt;
What this test does not prove&lt;br&gt;
Being honest about the limits, since that's what makes a smoke test useful rather than a false comfort:&lt;/p&gt;

&lt;p&gt;It doesn't mean your content is good, or that anyone will cite it.&lt;br&gt;
It doesn't tell you whether AI crawlers are actually allowed in — that's robots.txt, a separate check entirely.&lt;br&gt;
It doesn't replicate any specific product's retrieval and ranking pipeline. Nobody outside those companies can.&lt;br&gt;
It catches one specific, common, expensive failure: important content hiding behind client-side timing. That's it. That's enough to be worth 30 seconds.&lt;/p&gt;

&lt;p&gt;Run it on something today&lt;br&gt;
Try it on your own site. Then try it on a competitor's — that part is genuinely educational, and you'll find broken ones faster than you'd expect.&lt;/p&gt;

&lt;p&gt;If you'd rather not do it by hand, I maintain a set of &lt;a href="https://geoprompttracker.com/tools/ai-readiness-audit" rel="noopener noreferrer"&gt;free tools for this&lt;/a&gt; — no signup — that fetch server-rendered HTML the way a crawler would and score what's actually readable. But the curl one-liner above costs you nothing and catches most of it.&lt;/p&gt;

&lt;p&gt;The cheapness is the point.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to &lt;a href="https://dev.to/alexshev"&gt;@alexshev&lt;/a&gt; for the smoke-test framing and the four failure modes — this post is basically our comment thread, cleaned up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Your React site might be invisible to ChatGPT — here's how to check in 30 seconds</title>
      <dc:creator>Osama Mumtaz</dc:creator>
      <pubDate>Tue, 21 Jul 2026 16:33:20 +0000</pubDate>
      <link>https://dev.to/osama1malik/your-react-site-might-be-invisible-to-chatgpt-heres-how-to-check-in-30-seconds-35ao</link>
      <guid>https://dev.to/osama1malik/your-react-site-might-be-invisible-to-chatgpt-heres-how-to-check-in-30-seconds-35ao</guid>
      <description>&lt;p&gt;Quick test before you read on. Run this against one of your own content pages:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
curl -s https://yoursite.com/some-page | grep -i "a sentence from your main content"
If that sentence doesn't come back, then as far as most AI crawlers are concerned, it isn't on your page. And that's a bigger deal than it used to be.

Why this happens
Google spent a decade building a rendering pipeline: Googlebot fetches your HTML, queues the page, runs a headless Chrome render, executes your JavaScript, then indexes the result. It's expensive and slow, and Google eats that cost because search is its business.

The AI crawlers — GPTBot, ClaudeBot, PerplexityBot, and the rest — mostly don't do this. They fetch your raw HTML response and move on. No render queue, no JS execution. Rendering the whole web at scale isn't worth it to them when their goal is harvesting text.

The practical consequence is blunt: the AI web is the no-JavaScript web. A page that looks perfect in a browser can be an empty &amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt; to the crawler deciding whether you're worth citing.

Who this hits
Client-side-rendered SPAs (plain CRA-style React, Vue, Angular without SSR): the worst case — crawlers see a shell.
Content behind "load more" / infinite scroll: only the initial HTML is visible.
JS-injected sections on otherwise-fine pages: the sneaky one. Your article renders server-side, but the pricing table you hydrate from an API — the part that answers the question — is invisible.
Frameworks with SSR/SSG (Next.js, Nuxt, SvelteKit) and classic server-rendered stacks (WordPress, Rails, etc.) are generally fine. WordPress, for all the jokes, is more AI-legible than a slick client-rendered SPA.

The three ways to check
curl + grep (above) — is your text in the raw HTML?
DevTools with JS disabled — reload and see what survives.
Fetch it like a crawler does — I built a free AI Snippet Simulator (no signup) that pulls your server HTML the way GPTBot would and reports what a model can actually read and cite. If it says "too little readable text," you've found the problem — that verdict is the crawler's experience of your page.
The fix, cheapest first
Already server-rendered? Nothing to do — just verify the JS-injected widgets.
On a framework that supports SSR/SSG? Turn it on. SSG for content pages is ideal: complete HTML at build time, fast for humans, fully legible to every crawler.
Can't adopt SSR? Server-render at least the critical content — the H1, the opening answer, key facts — and let JS enhance from there.
Don't forget metadata: JS-injected &amp;lt;title&amp;gt;, meta descriptions, and JSON-LD have the same problem. If a tag manager adds your structured data client-side, crawlers never see it. It belongs in the server-rendered &amp;lt;head&amp;gt;.

The one exception
Google AI Overviews inherit Googlebot's rendering, so JS content Google indexes can surface there. That's why some SPAs appear in AI Overviews while being totally absent from ChatGPT and Perplexity — different pipelines, different capabilities. Treat Google as the exception and raw HTML as the baseline.

Curious how many people here have actually checked — if you curl your own site, is your main content in the response? Genuinely interested in how common the SPA blind spot is in practice.

*(Leave `canonical_url` blank so dev.to is canonical; you still get the in-body link + referral + dev.to's own ranking. Tags: `webdev, ai, javascript, seo` — the `javascript` tag reaches a big, different audience than your first post.)*
---
## Reddit post (fresh angle: training vs search — for r/Agentic_SEO)
Post this in **r/Agentic_SEO** (the sub where you found that build post — it's smaller, on-topic, and friendlier to this than r/TechSEO's shill filter). Keep it link-free; share the tool only if asked.
**Title:** PSA: blocking GPTBot doesn't remove you from ChatGPT — they're completely different crawlers
**Body:**
keep seeing people add `User-agent: GPTBot / Disallow: /` and think they've "blocked ChatGPT." they haven't, and it's worth understanding why.
OpenAI runs (at least) three separate crawlers:
- **GPTBot** — training. this is the one everyone blocks.
- **OAI-SearchBot** — builds the index behind ChatGPT's *search* answers.
- **ChatGPT-User** — fetches a page live when a user asks ChatGPT about it.
so blocking GPTBot opts you out of model *training*, but you're still fully visible in ChatGPT's search answers and live browsing. two different pipelines, two different user agents. i keep finding sites that think they've disappeared from ChatGPT when they're still getting cited in it daily.
the flip side is the more common mistake imo: people do a blanket "block all AI" and accidentally kill OAI-SearchBot + PerplexityBot — which are the ones that actually *send traffic*. so they've de-indexed themselves from the AI answers while keeping the training crawlers that give nothing back.
and it's not just OpenAI — Anthropic splits ClaudeBot (training) from Claude-SearchBot (search), Google splits Google-Extended (Gemini training, a robots token not even a crawler) from Googlebot, etc. almost every company separates "train on me" from "cite me," which means you can allow one and block the other on purpose.
how's everyone here thinking about this split? blocking training but keeping search open? all-or-nothing? i keep going back and forth for content-heavy sites and would love to hear how others land on it.
*(If someone asks "is there a list of all these?" → then reply with the directory link + "full disclosure it's mine.")*
---
**One process note:** the training-vs-search insight now appears in your dev.to piece, your Reddit post, *and* your site's guides — that's fine and deliberate (it's the single most useful counterintuitive fact in this niche, and repetition across channels is how a point sticks), but don't reuse the *exact same wording* on-site vs off-site, which these don't. Go reply to those PH comments first — that's the time-sensitive one.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
      <category>seo</category>
    </item>
    <item>
      <title>I checked which AI crawlers actually respect robots.txt — here's what I found</title>
      <dc:creator>Osama Mumtaz</dc:creator>
      <pubDate>Fri, 17 Jul 2026 18:26:32 +0000</pubDate>
      <link>https://dev.to/osama1malik/i-checked-which-ai-crawlers-actually-respect-robotstxt-heres-what-i-found-2269</link>
      <guid>https://dev.to/osama1malik/i-checked-which-ai-crawlers-actually-respect-robotstxt-heres-what-i-found-2269</guid>
      <description>&lt;p&gt;Most site owners have a &lt;code&gt;robots.txt&lt;/code&gt; that mentions Googlebot and maybe Bingbot, and nothing else. But there are now at least 15 AI crawlers hitting your site — some collecting training data, some powering live answers in ChatGPT and Perplexity — and they do &lt;strong&gt;not&lt;/strong&gt; all behave the way their documentation claims.&lt;/p&gt;

&lt;p&gt;I spent a while digging into how each one actually behaves and building free tools to check it, and a few things genuinely surprised me. Here's the rundown.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two jobs AI crawlers do (and why blocking one doesn't block the other)
&lt;/h2&gt;

&lt;p&gt;Before the compliance stuff, the single most common misconception: "blocking ChatGPT."&lt;/p&gt;

&lt;p&gt;AI companies run &lt;strong&gt;separate crawlers for separate jobs&lt;/strong&gt;, under different user-agent strings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Training crawlers&lt;/strong&gt; collect content to train future models. &lt;code&gt;GPTBot&lt;/code&gt; (OpenAI), &lt;code&gt;ClaudeBot&lt;/code&gt; (Anthropic), &lt;code&gt;CCBot&lt;/code&gt; (Common Crawl).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search / live crawlers&lt;/strong&gt; fetch pages so an assistant can cite them in a live answer. &lt;code&gt;OAI-SearchBot&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;, &lt;code&gt;ChatGPT-User&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So when someone adds this to block "ChatGPT":&lt;br&gt;
User-agent: GPTBot&lt;br&gt;
Disallow: /&lt;/p&gt;

&lt;p&gt;...they've opted out of &lt;strong&gt;training&lt;/strong&gt;, but they're still fully visible in ChatGPT's &lt;strong&gt;search&lt;/strong&gt; answers, which use &lt;code&gt;OAI-SearchBot&lt;/code&gt;. Different pipeline, different user agent. Blocking one has zero effect on the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The compliance findings
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. Not every crawler honors &lt;code&gt;robots.txt&lt;/code&gt;, and the docs don't always match reality:&lt;br&gt;
| Crawler | Operator | Respects robots.txt? |&lt;br&gt;
|---|---|---|&lt;br&gt;
| GPTBot | OpenAI | ✅ Yes (documented) |&lt;br&gt;
| OAI-SearchBot | OpenAI | ✅ Yes |&lt;br&gt;
| ClaudeBot | Anthropic | ✅ Yes |&lt;br&gt;
| CCBot | Common Crawl | ✅ Yes |&lt;br&gt;
| Google-Extended | Google | ✅ Yes (it's a &lt;em&gt;token&lt;/em&gt;, not a crawler — more below) |&lt;br&gt;
| Applebot-Extended | Apple | ✅ Yes (also a token) |&lt;br&gt;
| PerplexityBot | Perplexity | ⚠️ Partial — Cloudflare reported undeclared fetching that bypassed blocks |&lt;br&gt;
| Perplexity-User | Perplexity | ❌ No — skips robots.txt &lt;strong&gt;by design&lt;/strong&gt; |&lt;br&gt;
| Bytespider | ByteDance | ❌ No — widely reported to ignore it entirely |&lt;br&gt;
| cohere-ai | Cohere | ❓ Undocumented — can't verify |&lt;br&gt;
Three things worth calling out:&lt;br&gt;
&lt;strong&gt;1. &lt;code&gt;Bytespider&lt;/code&gt; (ByteDance) just doesn't care.&lt;/strong&gt; No published documentation, crawls aggressively, and multiple independent reports show it fetching pages on sites that disallowed it. If you actually want it gone, &lt;code&gt;robots.txt&lt;/code&gt; won't do it — you need a firewall/CDN rule matching the user agent.&lt;br&gt;
&lt;strong&gt;2. &lt;code&gt;Perplexity-User&lt;/code&gt; ignores robots.txt on purpose.&lt;/strong&gt; Perplexity's own docs say so: because the fetch is triggered directly by a user asking a question, they treat it like a browser, not a crawler. Reasonable framing, but it means "block Perplexity via robots.txt" is only half true.&lt;br&gt;
&lt;strong&gt;3. &lt;code&gt;Google-Extended&lt;/code&gt; and &lt;code&gt;Applebot-Extended&lt;/code&gt; aren't crawlers at all.&lt;/strong&gt; They're &lt;em&gt;product tokens&lt;/em&gt;. You'll never see them in your logs. Regular Googlebot/Applebot fetches your pages as always; the token just governs whether that already-crawled content may train Gemini / Apple Intelligence. Which leads to the mistake I see most often...&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake: thinking Google-Extended controls AI Overviews
&lt;/h2&gt;

&lt;p&gt;It doesn't. Blocking &lt;code&gt;Google-Extended&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Opts you out of &lt;strong&gt;Gemini training&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;❌ Does &lt;strong&gt;nothing&lt;/strong&gt; to AI Overviews (those are a Search feature, built from the Search index)&lt;/li&gt;
&lt;li&gt;❌ Has &lt;strong&gt;zero&lt;/strong&gt; effect on your Google rankings
There's no clean "opt out of AI Overviews" switch — the only levers are &lt;code&gt;nosnippet&lt;/code&gt; / &lt;code&gt;max-snippet&lt;/code&gt;, which also shrink your normal search snippets. So blocking Google-Extended is a rare &lt;em&gt;free&lt;/em&gt; opt-out: you lose nothing in Search, you just leave Gemini's training set.
## The other thing that quietly breaks AI visibility: JavaScript
Here's one that catches SPAs hard. &lt;strong&gt;Most AI crawlers don't execute JavaScript.&lt;/strong&gt; Unlike Googlebot — which has a full headless-Chrome rendering pipeline — GPTBot, ClaudeBot, and PerplexityBot mostly fetch your raw HTML and move on.
So if your content is client-side rendered:&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
html
&amp;lt;body&amp;gt;
  &amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;!-- content injected by JS after load --&amp;gt;
&amp;lt;/body&amp;gt;
...that's roughly what the AI web sees of your site: an empty shell. The quick test:

curl -s https://yoursite.com/your-page | grep -i "a phrase from your main content"
If the phrase isn't in the response, it isn't in your server-rendered HTML, and AI crawlers can't read it. The fix is SSR/SSG (Next.js, Nuxt, etc.) or pre-rendering for bot user agents. Google is basically the only exception here — assume everything else reads raw HTML only.

What I built out of all this
I got tired of not being able to answer basic questions — can ChatGPT even see this site? which bots am I accidentally blocking? — and the existing "AI SEO" tools are mostly $30–500/mo SaaS. So I built free browser-based versions of the pieces I needed and put them at geoprompttracker.com:

A directory of every AI crawler with the compliance notes above and copy-paste allow/block rules per bot
An AI Crawler Access Checker that reads your live robots.txt and shows which of the 15 bots you allow or block
An llms.txt generator/validator for the emerging llms.txt convention
A robots.txt generator with per-bot toggles (block training, allow search — or whatever split you want)
Stack, for the curious: Next.js App Router on Vercel, everything static or client-side, no accounts, no signup. The few tools that fetch a URL go through a rate-limited proxy with an SSRF guard; nothing is stored.

The 5-minute audit, if you do nothing else
Decide your policy: training vs. search, per operator. They're separate choices.
Check what your robots.txt currently says with a crawler checker — a lot of "block all AI" configs accidentally kill Perplexity/OAI-SearchBot, which are the ones that send you traffic.
curl a key page and confirm your content is in the raw HTML, not JS-injected.
For the bots that ignore robots.txt (Bytespider, Perplexity-User), use firewall rules if you actually care.
I'd genuinely like feedback on the bot-compliance data — if you've got server logs showing different behavior (especially anything that's stopped respecting robots.txt recently), I'd love to hear it. This space changes month to month and I'm refreshing the list as it does.

---
### Two small notes before you hit publish
1. **Leave `canonical_url` unset.** This article is original to dev.to, so let dev.to be canonical — you still get the backlinks in the body + referral traffic + dev.to's own Google ranking pointing readers to you. (Only set a canonical if you were reposting something already on your site.)
2. **Tags:** I used `webdev, ai, seo, programming` — all have real audiences on dev.to. You can swap `programming` for `nextjs` if you'd rather reach that crowd.
After it's live, drop the link in the r/TechSEO post too — the article and the `/bots` directory reinforce each other. Want me to draft the **r/TechSEO post** next so you can fire both today while the HN account warms up?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>seo</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
