<?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: Gabriele</title>
    <description>The latest articles on DEV Community by Gabriele (@gabriele_955496641624d8d4).</description>
    <link>https://dev.to/gabriele_955496641624d8d4</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%2F3818235%2F33cdb316-80cc-4eef-84a0-b619857498da.png</url>
      <title>DEV Community: Gabriele</title>
      <link>https://dev.to/gabriele_955496641624d8d4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gabriele_955496641624d8d4"/>
    <language>en</language>
    <item>
      <title>The AI crawlers in your logs, and what blocking each one actually costs</title>
      <dc:creator>Gabriele</dc:creator>
      <pubDate>Thu, 03 Sep 2026 21:31:31 +0000</pubDate>
      <link>https://dev.to/gabriele_955496641624d8d4/the-ai-crawlers-in-your-logs-and-what-blocking-each-one-actually-costs-3c9</link>
      <guid>https://dev.to/gabriele_955496641624d8d4/the-ai-crawlers-in-your-logs-and-what-blocking-each-one-actually-costs-3c9</guid>
      <description>&lt;p&gt;Sooner or later someone forwards you a screenshot of the access log and asks whether to block "the AI bots". It is a reasonable question with an unreasonable premise: there is no single population there. There are at least three, they want different things, and blocking them costs you different things.&lt;/p&gt;

&lt;p&gt;Here is the map I wish I had the first time I was asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three jobs, not one
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Training crawlers&lt;/strong&gt; collect pages to train a future model. Nothing you do today changes a model that already shipped, and nothing about your search ranking depends on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live-fetch bots&lt;/strong&gt; fetch one page because a user asked something &lt;em&gt;right now&lt;/em&gt;. Block these and you disappear from the answer that was about to cite you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer-engine indexers&lt;/strong&gt; build an index used to answer questions with citations. Closest thing to search, and the one where blocking most directly costs you referral traffic.&lt;/p&gt;

&lt;p&gt;The same company usually runs one of each, under different names. That is the part people miss.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;User-agent&lt;/th&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;What blocking it costs you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GPTBot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;Training&lt;/td&gt;
&lt;td&gt;Nothing in the short term&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OAI-SearchBot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;Search index for ChatGPT&lt;/td&gt;
&lt;td&gt;Visibility in ChatGPT search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ChatGPT-User&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;td&gt;Live fetch on user request&lt;/td&gt;
&lt;td&gt;Your page cannot be opened when a user asks about it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ClaudeBot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anthropic&lt;/td&gt;
&lt;td&gt;Training&lt;/td&gt;
&lt;td&gt;Nothing in the short term&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Claude-Web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anthropic&lt;/td&gt;
&lt;td&gt;Live fetch&lt;/td&gt;
&lt;td&gt;Same as &lt;code&gt;ChatGPT-User&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PerplexityBot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Perplexity&lt;/td&gt;
&lt;td&gt;Index for cited answers&lt;/td&gt;
&lt;td&gt;Citations, and the referral traffic with them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Google-Extended&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;Gemini training only&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Nothing in Search — see below&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CCBot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Common Crawl&lt;/td&gt;
&lt;td&gt;Open dataset many models train on&lt;/td&gt;
&lt;td&gt;Nothing directly; wide indirect reach&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Bytespider&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ByteDance&lt;/td&gt;
&lt;td&gt;Training&lt;/td&gt;
&lt;td&gt;Nothing in the short term&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Applebot-Extended&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apple&lt;/td&gt;
&lt;td&gt;Apple Intelligence training&lt;/td&gt;
&lt;td&gt;Nothing in Search — &lt;code&gt;Applebot&lt;/code&gt; is separate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Amazonbot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Amazon&lt;/td&gt;
&lt;td&gt;Assistant and search&lt;/td&gt;
&lt;td&gt;Alexa-surface visibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;meta-externalagent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;Training&lt;/td&gt;
&lt;td&gt;Nothing in the short term&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The one that causes the most damage by accident
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Google-Extended&lt;/code&gt; is not Googlebot.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It controls whether your content is used to train Gemini. It has no effect on crawling for Search, on indexing, or on ranking. Blocking it does not deindex you and does not cost you a position.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Googlebot&lt;/code&gt; is what crawls for Search. If you block &lt;em&gt;that&lt;/em&gt; one, you leave the index.&lt;/p&gt;

&lt;p&gt;I have seen the two conflated in both directions: people who avoid blocking training because they fear losing rankings they were never going to lose, and — worse — people who block &lt;code&gt;Googlebot&lt;/code&gt; believing they are opting out of AI. The names are similar; the consequences are not.&lt;/p&gt;

&lt;p&gt;Same pattern at Apple: &lt;code&gt;Applebot&lt;/code&gt; crawls for Siri and Spotlight, &lt;code&gt;Applebot-Extended&lt;/code&gt; is the training opt-out. Different bots, different consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trade-off nobody can settle for you
&lt;/h2&gt;

&lt;p&gt;For scale, we crawled the robots.txt of a large sample of sites for &lt;a href="https://quanticdata.io/blog/should-i-block-ai-crawlers/" rel="noopener noreferrer"&gt;a study on this exact question&lt;/a&gt;: 17.8% block GPTBot against 2.5% for Googlebot, and 12.3% block PerplexityBot, a crawler that does not train models at all. That last figure is the accidental-damage bucket, measured in the wild.&lt;/p&gt;

&lt;p&gt;The argument for blocking training crawlers is simple: your content is an asset, training on it is a use, and you get nothing back.&lt;/p&gt;

&lt;p&gt;The argument against is subtler. Assistants that cite sources send traffic, and increasingly they &lt;em&gt;are&lt;/em&gt; the search interface. A site that is invisible to them is invisible in a place where people now ask questions. And the distinction between "training" and "answering" is blurrier in practice than the bot names suggest.&lt;/p&gt;

&lt;p&gt;The position most people land on, once it is laid out this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Block training&lt;/strong&gt; — &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, &lt;code&gt;CCBot&lt;/code&gt;, &lt;code&gt;Google-Extended&lt;/code&gt;, &lt;code&gt;Bytespider&lt;/code&gt;, &lt;code&gt;Applebot-Extended&lt;/code&gt;, &lt;code&gt;meta-externalagent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allow live-fetch and answer indexes&lt;/strong&gt; — &lt;code&gt;ChatGPT-User&lt;/code&gt;, &lt;code&gt;Claude-Web&lt;/code&gt;, &lt;code&gt;OAI-SearchBot&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never touch&lt;/strong&gt; &lt;code&gt;Googlebot&lt;/code&gt;, &lt;code&gt;Bingbot&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which comes out as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;GPTBot&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;ClaudeBot&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;CCBot&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;Google&lt;/span&gt;-&lt;span class="n"&gt;Extended&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;Bytespider&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;Applebot&lt;/span&gt;-&lt;span class="n"&gt;Extended&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;meta&lt;/span&gt;-&lt;span class="n"&gt;externalagent&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;Sitemap&lt;/span&gt;: &lt;span class="n"&gt;https&lt;/span&gt;://&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;/&lt;span class="n"&gt;sitemap&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a position, not a default. A documentation site that wants to be the answer everywhere might block nothing. A publisher whose archive &lt;em&gt;is&lt;/em&gt; the product might block everything. Both are coherent; drifting into one by accident is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things to know before you edit the file
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A specific group replaces the wildcard group — it does not extend it.&lt;/strong&gt; If you write a group for &lt;code&gt;GPTBot&lt;/code&gt;, that group is the whole ruleset for GPTBot. Rules under &lt;code&gt;User-agent: *&lt;/code&gt; are not inherited. This is the most common way a hand-written file ends up doing the opposite of what was intended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;robots.txt is a request, not a fence.&lt;/strong&gt; Well-behaved crawlers obey it. Nothing enforces it. If a bot must be stopped rather than asked, that is a job for authentication or blocking at the edge, and the operator list above is the wrong tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test the file before you ship it.&lt;/strong&gt; The precedence rules are unintuitive: the &lt;em&gt;longest&lt;/em&gt; matching rule wins, not the first, and on a tie &lt;code&gt;Allow&lt;/code&gt; beats &lt;code&gt;Disallow&lt;/code&gt;. A file that reads correctly to a human can behave differently to a parser.&lt;/p&gt;




&lt;p&gt;I maintain two small browser-based tools for exactly this: a &lt;a href="https://quanticdata.io/tools/robots-txt-generator/" rel="noopener noreferrer"&gt;robots.txt generator&lt;/a&gt; with the AI-crawler presets above, and a &lt;a href="https://quanticdata.io/tools/robots-txt-tester/" rel="noopener noreferrer"&gt;tester&lt;/a&gt; that shows which line decided each verdict. Neither uploads anything — both run on the page.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>seo</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The robots.txt rule almost every tester gets wrong</title>
      <dc:creator>Gabriele</dc:creator>
      <pubDate>Thu, 03 Sep 2026 21:30:39 +0000</pubDate>
      <link>https://dev.to/gabriele_955496641624d8d4/the-robotstxt-rule-almost-every-tester-gets-wrong-5d8a</link>
      <guid>https://dev.to/gabriele_955496641624d8d4/the-robotstxt-rule-almost-every-tester-gets-wrong-5d8a</guid>
      <description>&lt;p&gt;Here is a robots.txt. Is &lt;code&gt;/private/report.pdf&lt;/code&gt; crawlable?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;private&lt;/span&gt;/
&lt;span class="n"&gt;Allow&lt;/span&gt;: /&lt;span class="n"&gt;private&lt;/span&gt;/&lt;span class="n"&gt;report&lt;/span&gt;.&lt;span class="n"&gt;pdf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you answered "no, it's under &lt;code&gt;/private/&lt;/code&gt;", you have company — including, in my experience, a few online testers. The correct answer is &lt;strong&gt;yes&lt;/strong&gt;, and the reason is a rule that is easy to state and easy to implement backwards.&lt;/p&gt;

&lt;p&gt;I ran into this while writing a robots.txt tester, which is a good way to discover that you did not understand a spec as well as you thought.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three rules that decide everything
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9309.html" rel="noopener noreferrer"&gt;RFC 9309&lt;/a&gt; standardised what Google's parser had been doing for years. Three rules, applied in order:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. One group wins, and only one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A crawler scans the file for the group whose &lt;code&gt;User-agent&lt;/code&gt; names it most specifically. &lt;code&gt;User-agent: *&lt;/code&gt; is a fallback, not a base class. If a group names the bot directly, the wildcard group is ignored &lt;em&gt;entirely&lt;/em&gt; — its rules are not merged in, not inherited, not consulted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;GPTBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GPTBot may crawl everything. It does not "inherit" the &lt;code&gt;Disallow: /&lt;/code&gt;. This surprises people who read the file top-down like a firewall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The longest matching rule wins — not the first.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the one in the example above. &lt;code&gt;/private/&lt;/code&gt; is 9 characters, &lt;code&gt;/private/report.pdf&lt;/code&gt; is 19. The longer pattern matches, so the &lt;code&gt;Allow&lt;/code&gt; decides, and that single file stays crawlable while the rest of the folder does not.&lt;/p&gt;

&lt;p&gt;Order in the file is irrelevant. You can put the &lt;code&gt;Allow&lt;/code&gt; first, last, or between two unrelated lines; the outcome is the same. An implementation that returns the first match will disagree with Google on every file that uses this pattern — and it is a common pattern, because it is the only way to expose one file from a blocked directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. On a tie, &lt;code&gt;Allow&lt;/code&gt; wins.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /&lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same length, both match. &lt;code&gt;/a&lt;/code&gt; is crawlable. This is the case I would bet most hand-rolled parsers get wrong, because it only shows up if you deliberately test for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A test file for your tester
&lt;/h2&gt;

&lt;p&gt;Paste this into whatever tool or library you rely on, then check the verdicts against the right-hand column. It takes a minute and it is worth doing before you trust a tool with a migration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;private&lt;/span&gt;/
&lt;span class="n"&gt;Allow&lt;/span&gt;: /&lt;span class="n"&gt;private&lt;/span&gt;/&lt;span class="n"&gt;report&lt;/span&gt;.&lt;span class="n"&gt;pdf&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /&lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /*.&lt;span class="n"&gt;pdf&lt;/span&gt;$

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;GPTBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;Correct verdict&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/private/notes.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;td&gt;Only &lt;code&gt;Disallow: /private/&lt;/code&gt; matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/private/report.pdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Allowed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Longer &lt;code&gt;Allow&lt;/code&gt; beats shorter &lt;code&gt;Disallow&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Allowed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Equal length, &lt;code&gt;Allow&lt;/code&gt; wins the tie&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/docs/manual.pdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/*.pdf$&lt;/code&gt; matches, &lt;code&gt;$&lt;/code&gt; anchors the end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/docs/manual.pdf.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allowed&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;$&lt;/code&gt; anchors — the path does not end in &lt;code&gt;.pdf&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/private/notes.txt&lt;/code&gt; as GPTBot&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Allowed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Specific group replaces the wildcard group entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your tool disagrees on rows 2, 3 or 6, it is not implementing RFC 9309, and its verdicts will drift from reality exactly where it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two more things that bite
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;An empty &lt;code&gt;Disallow:&lt;/code&gt; restricts nothing.&lt;/strong&gt; It is the standard way of saying "everything is open". More than once I have seen it read as "block everything" — the opposite of the truth. &lt;code&gt;Disallow: /&lt;/code&gt; with the slash is what blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;*&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; are the only wildcards.&lt;/strong&gt; No regex, no character classes. &lt;code&gt;*&lt;/code&gt; matches any run of characters, &lt;code&gt;$&lt;/code&gt; anchors the end of the path. Everything else is a literal, including &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;.&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this matters more than it used to
&lt;/h2&gt;

&lt;p&gt;The rules above have been stable for years. What changed is who is reading the file.&lt;/p&gt;

&lt;p&gt;A robots.txt now governs at least three different populations, and they are separate user-agents doing separate jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search crawlers&lt;/strong&gt; — &lt;code&gt;Googlebot&lt;/code&gt;, &lt;code&gt;Bingbot&lt;/code&gt;. They decide whether you appear in search results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI training crawlers&lt;/strong&gt; — &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, &lt;code&gt;CCBot&lt;/code&gt;, &lt;code&gt;Google-Extended&lt;/code&gt;, &lt;code&gt;Bytespider&lt;/code&gt;. They collect pages to train models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live-fetch bots&lt;/strong&gt; — &lt;code&gt;ChatGPT-User&lt;/code&gt;, &lt;code&gt;Claude-Web&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;. They fetch a page because a user asked something right now.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The distinction that trips people up most: &lt;strong&gt;&lt;code&gt;Google-Extended&lt;/code&gt; controls Gemini training and has no effect whatsoever on Google Search ranking.&lt;/strong&gt; Blocking it does not deindex you. &lt;code&gt;Googlebot&lt;/code&gt; is what crawls for Search. They are different bots with different rules, and a group naming one says nothing about the other.&lt;/p&gt;

&lt;p&gt;Which means a file like this is coherent, and increasingly common:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;GPTBot&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;CCBot&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;Google&lt;/span&gt;-&lt;span class="n"&gt;Extended&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Search engines crawl everything. Training crawlers get nothing. Nothing about your rankings changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What robots.txt still cannot do
&lt;/h2&gt;

&lt;p&gt;It is a crawling instruction. It is not an access control and not an indexing control.&lt;/p&gt;

&lt;p&gt;A disallowed URL can still appear in search results if other sites link to it — the crawler never fetches the page, so it never sees your &lt;code&gt;noindex&lt;/code&gt;, and it can list the URL from the link alone. If you want a page out of the index, &lt;strong&gt;allow&lt;/strong&gt; the crawl and serve &lt;code&gt;noindex&lt;/code&gt;. If you want it private, require authentication. And robots.txt is a public file: writing &lt;code&gt;Disallow: /secret-admin/&lt;/code&gt; publishes the existence of &lt;code&gt;/secret-admin/&lt;/code&gt; to anyone who looks.&lt;/p&gt;




&lt;p&gt;I built a &lt;a href="https://quanticdata.io/tools/robots-txt-tester/" rel="noopener noreferrer"&gt;robots.txt tester&lt;/a&gt; that implements the precedence above and shows you the exact line that decided each verdict, including the AI crawlers. It runs entirely in the browser, so you can paste a file that is not published yet. The six rows in the table are its test suite.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Debugging a Python requests proxy issue: works in curl but fails in script</title>
      <dc:creator>Gabriele</dc:creator>
      <pubDate>Wed, 11 Mar 2026 10:00:02 +0000</pubDate>
      <link>https://dev.to/gabriele_955496641624d8d4/debugging-a-python-requests-proxy-issue-works-in-curl-but-fails-in-script-5h2k</link>
      <guid>https://dev.to/gabriele_955496641624d8d4/debugging-a-python-requests-proxy-issue-works-in-curl-but-fails-in-script-5h2k</guid>
      <description>&lt;p&gt;While debugging a Python script that sends HTTP requests through a proxy, I ran into a strange issue.&lt;/p&gt;

&lt;p&gt;The proxy works perfectly when tested with curl:&lt;/p&gt;

&lt;p&gt;curl -x &lt;a href="http://user:pass@proxy:port" rel="noopener noreferrer"&gt;http://user:pass@proxy:port&lt;/a&gt; &lt;a href="https://quantumproxies.io" rel="noopener noreferrer"&gt;https://quantumproxies.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This returns the correct IP, so the proxy itself is clearly working.&lt;/p&gt;

&lt;p&gt;However the equivalent Python code fails:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
python
import requests

proxy = "http://user:pass@proxy:port"

proxies = {
    "http": proxy,
    "https": proxy
}

url = "https://httpbin.org/ip"

response = requests.get(
    url,
    proxies=proxies
    timeout=10
)

print(response.json())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>python</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
