Nasza rezerwacja praw do trenowania AI była napisana do nikogo. Linia Content-Signal: search=yes, ai-input=yes, ai-train=no stała w pliku robots.txt wppoland.com dokładnie raz, w grupie User-agent: *, a poniżej niej leżało dwadzieścia nazwanych grup crawlerów. Zgodnie z RFC 9309 żadna z tych dwudziestu grup nie dziedziczy niczego z grupy gwiazdki. GPTBot, ClaudeBot, PerplexityBot, Google-Extended i reszta listy czytały wyłącznie swoje własne sekcje, w których tej linii nie było.
Piszemy o tym, bo to nasz plik i nasz błąd. Sprawdzaliśmy robots.txt pod kątem tego, czy nie blokuje czegoś przypadkiem, i nigdy pod kątem tego, czy deklaracja w ogóle trafia do adresata.
Co dokładnie było w pliku
Struktura wyglądała tak, jak wygląda w większości plików, które widzieliśmy: jedna ogólna grupa na górze, a pod nią lista nazwanych botów z rozszerzonymi regułami Allow dla endpointów maszynowych.
User-agent: *
Content-Signal: search=yes, ai-input=yes, ai-train=no
Allow: /
Disallow: /cdn-cgi/
Sitemap: https://wppoland.com/sitemap-index.xml
Agentmap: https://wppoland.com/.well-known/ai-catalog.json
User-agent: GPTBot
Allow: /
Allow: /llms.txt
Allow: /facts.json
User-agent: ClaudeBot
Allow: /
User-agent: PerplexityBot
Allow: /Wygląda porządnie. Sygnał jest, nazwane grupy są, endpointy dla agentów są wypisane osobno. Problem polega na tym, że te dwie rzeczy nie mają ze sobą kontaktu.
Dlaczego jedna linia nie obejmuje całego pliku
RFC 9309 opisuje dopasowanie grup w sposób, który łatwo przeczytać jako oczywisty, dopóki nie zestawi się go z własnym plikiem. Klient porównuje swój token produktu z nazwami w liniach User-agent, bez rozróżniania wielkości liter, i stosuje jedną grupę: tę, która go nazywa. Gwiazdka nie jest wzorcem najmniej szczegółowym, tylko zapasową: RFC 9309 sięga po nią wyłącznie wtedy, gdy żadna grupa nie wymienia klienta z nazwy. Gdy nazwana grupa istnieje, klient stosuje wyłącznie jej dyrektywy i grupa gwiazdki przestaje go dotyczyć w całości.
To nie jest wyjątek dla Content Signals. Ta sama zasada od zawsze obowiązuje Allow i Disallow i dokładnie dlatego plik, w którym ktoś dopisał Disallow: /wp-admin/ tylko do grupy gwiazdki, nie chroni niczego przed botem, który ma swoją sekcję niżej. Różnica jest taka, że przy Disallow konsekwencję widać: strona pojawia się w indeksie albo nie. Przy Content-Signal nie widać nic. Deklaracja praw nie ma sprzężenia zwrotnego, więc może być błędna przez wiele miesięcy i wyglądać identycznie jak poprawna.
Content Signals to propozycja Cloudflare, a nie część RFC 9309. Cloudflare włącza tę politykę domyślnie w swoim zarządzanym pliku robots.txt. Ale mechanizm grup, w którym ta linia mieszka, jest w całości mechanizmem z RFC 9309, więc zasada jednej grupy obowiązuje ją tak samo jak wszystko inne w tym pliku. Nowa dyrektywa w starym kontenerze dziedziczy zasady kontenera.
Trzy sygnały i to, czego nie obiecują
Specyfikacja definiuje trzy sygnały, każdy z wartością yes albo no:
| Sygnał | Czego dotyczy |
|---|---|
search | budowanie indeksu wyszukiwarki i zwracanie wyników, czyli odnośników i krótkich fragmentów |
ai-input | podawanie treści na wejście modelu AI w czasie odpowiadania, czyli RAG i ugruntowanie odpowiedzi |
ai-train | trenowanie i dostrajanie modeli |
Nasz zestaw search=yes, ai-input=yes, ai-train=no jest świadomym wyborem. Chcemy być cytowani w odpowiedziach generatywnych, bo to dziś jeden z kanałów, którym ktoś nas znajduje, i nie chcemy oddawać korpusu do treningu. Brak sygnału oznacza coś trzeciego niż no: operator strony ani nie udziela zgody, ani jej nie odmawia tą drogą.
Żaden z tych sygnałów niczego nie blokuje. To linia tekstu w pliku, który klient może pobrać albo nie, przeczytać albo nie i uszanować albo nie. Serwer odpowiada na żądanie niezależnie od tego, co w tym pliku napisano. Blokowanie jest osobną warstwą, ocenianą przed aplikacją, i robi się je regułami WAF, limitami zapytań albo weryfikacją tożsamości bota, nie plikiem tekstowym. Sygnał ma wartość dokładnie taką, jak jednoznaczny komunikat wysłany pod właściwy adres. Nasz był jednoznaczny i wysłany pod zły.
Sprawdź własny plik jedną komendą
To zajmuje kilka sekund i działa na dowolnej domenie:
curl -s https://twojadomena.pl/robots.txt \
| awk 'tolower($0) ~ /^user-agent:/ {ua++} tolower($0) ~ /^content-signal:/ {cs++} END {print "grupy User-agent: " ua "\nlinie Content-Signal: " cs}'Jedno zastrzeżenie, zanim zadziałasz na podstawie wyniku: komenda liczy linie User-agent, a nie grupy, a ABNF z RFC 9309 dopuszcza kilka linii User-agent otwierających jedną grupę. Plik, który tak układa nazwy, zgłosi tu braki, których nie ma, i wtedy wiążąca jest lista z drugiej komendy, a nie arytmetyka z pierwszej.
Jeśli liczba grup jest większa od liczby sygnałów, to różnica mówi, ile grup nie widzi twojej rezerwacji. U nas wynik brzmiał dwadzieścia jeden do jednego.
Dokładniejszy wariant wypisuje nazwy grup, w których sygnału brakuje:
curl -s https://twojadomena.pl/robots.txt \
| awk '/^[Uu]ser-agent:/ {if (name != "" && !sig) print "brak sygnalu: " name; name=$2; sig=0} /^[Cc]ontent-[Ss]ignal:/ {sig=1} END {if (name != "" && !sig) print "brak sygnalu: " name}'Druga komenda jest tą, którą warto wpiąć w pipeline, bo jej wyjście jest listą do naprawienia, a nie liczbą do zinterpretowania.
Jak to naprawić
Poprawka jest nudna: powtórzyć linię Content-Signal w każdej grupie, której ma dotyczyć. Nie ma tu skrótu, formy skróconej ani dziedziczenia, które można włączyć.
User-agent: *
Content-Signal: search=yes, ai-input=yes, ai-train=no
Allow: /
Disallow: /cdn-cgi/
User-agent: GPTBot
Content-Signal: search=yes, ai-input=yes, ai-train=no
Allow: /
Allow: /llms.txt
Allow: /facts.json
User-agent: ClaudeBot
Content-Signal: search=yes, ai-input=yes, ai-train=no
Allow: /
User-agent: PerplexityBot
Content-Signal: search=yes, ai-input=yes, ai-train=no
Allow: /Tę zmianę wdrożyliśmy 18 września 2026, w dniu publikacji tego wpisu. Sygnał stoi teraz w 18 grupach na 23, a pięć pominiętych to scrapery, które i tak mają u nas Disallow.
Przy okazji poprawki trzeba rozstrzygnąć jedną rzecz, której plik z powtórzoną linią nie rozstrzyga sam: czy ten sam zestaw sygnałów pasuje do każdej grupy. Dla crawlera indeksującego i dla crawlera zbierającego korpus treningowy odpowiedź jest inna, a plik z dwudziestoma identycznymi liniami sugeruje, że nikt się nad tym nie zastanawiał. Grupa, w której search=yes nie ma sensu, bo klient nie buduje indeksu, powinna mieć swój własny zapis.
Drugie, czego nam brakowało: grupy dla poszczególnych ról
Przy tej samej lekturze pliku wyszło, że mieliśmy samą grupę ClaudeBot, a nie mieliśmy dwóch pozostałych: Claude-User, obsługującej pobrania inicjowane przez użytkownika, i Claude-SearchBot, obsługującej ugruntowanie wyszukiwania. To trzy różne role i różne intencje po stronie klienta. Bot, który pobiera stronę, bo człowiek właśnie wkleił jej adres w rozmowie, robi coś innego niż bot budujący korpus, i sensownie jest odpowiedzieć mu czymś innym.
Efekt braku tych grup jest dokładnie ten sam co wyżej, tylko od drugiej strony: klient, który nie ma swojej sekcji, wraca do grupy gwiazdki. W naszym przypadku to akurat oznaczało, że Claude-User widział poprawny sygnał, bo w gwiazdce on był. Czyli w pliku, w którym sygnał nie docierał do dwudziestu nazwanych botów, docierał do tych nienazwanych. Dokładnie odwrotnie niż zamierzaliśmy.
Dlaczego ten błąd tak łatwo przeżywa przegląd
Trzy powody, wszystkie strukturalne, żaden z nich to nieuwaga.
Plik robots.txt czyta się z góry na dół i sekcja ogólna wygląda na nadrzędną. Wcięcie w nią czegokolwiek uruchamia intuicję z CSS albo z konfiguracji serwera, gdzie ogólne ustawienie jest domyślne, a szczegółowe je nadpisuje. W RFC 9309 nie ma nadpisywania. Jest wybór jednej grupy i odrzucenie reszty pliku.
Drugi powód: nie ma narzędzia, które by to zgłosiło. Testery robots.txt sprawdzają, czy dany adres jest dozwolony dla danego bota, i o Content-Signal nie wiedzą nic, bo to nie jest dyrektywa z RFC. Linia przechodzi jako komentarz w oczach walidatora i jako polityka w oczach człowieka.
Trzeci, i najważniejszy: deklaracja praw nie ma sygnału zwrotnego. Gdy błędnie postawisz Disallow, po tygodniu widzisz to w raporcie indeksowania. Gdy błędnie postawisz Content-Signal, nie dzieje się nic, co dałoby się zaobserwować, a brak zdarzenia wygląda identycznie w obu przypadkach. Tę klasę defektów trzeba znajdować przez czytanie pliku, nie przez patrzenie na skutki.
Linie spoza grup mylą, bo one akurat obowiązują globalnie
Jest w tym pliku rzecz, która wprost podpowiada złą intuicję. Obok grup User-agent robots.txt zna rekordy niezależne od grup, i Sitemap jest takim rekordem. W naszym pliku stoi on po grupie gwiazdki:
Sitemap: https://wppoland.com/sitemap-index.xml
Agentmap: https://wppoland.com/.well-known/ai-catalog.jsonSitemap obowiązuje cały plik niezależnie od tego, gdzie go postawisz i ile grup jest pod nim. Nikt nie powtarza go dwadzieścia razy i nikt nie musi. Człowiek, który wie, jak zachowuje się Sitemap, i który widzi Content-Signal postawione w tym samym rejonie pliku, dwa wiersze wyżej, ma pełne prawo założyć, że ta linia zachowuje się tak samo. Nie zachowuje się. Jedna jest rekordem globalnym, druga dyrektywą wewnątrz grupy, i nic w składni tego nie sygnalizuje. Obie to dwuczłonowa linia z dwukropkiem, postawiona obok siebie.
Praktyczny wniosek przy czytaniu cudzego pliku: wcięcie i kolejność nic nie znaczą, znaczy wyłącznie to, czy dana dyrektywa jest zdefiniowana jako grupowa. Allow, Disallow i Content-Signal są grupowe. Sitemap nie jest.
Czego ten wpis nie rozstrzyga
Dwie rzeczy zostawiamy poza zakresem świadomie, bo nie są nasze.
Pierwsza to skutek prawny rezerwacji. Content Signals odwołuje się do rezerwacji praw wyrażonej w prawie autorskim i taki tekst rzeczywiście stoi w komentarzu na górze naszego pliku. Czy i kiedy ta rezerwacja jest skuteczna, rozstrzyga prawnik, a nie osoba edytująca robots.txt. Nasz jedyny wkład jest techniczny: jeśli deklaracja ma mieć jakikolwiek ciężar, to musi przynajmniej dotrzeć do klienta, do którego jest adresowana, a nasza nie docierała.
Druga to zachowanie konkretnych operatorów. Nie mierzyliśmy, który crawler czyta Content-Signal, który go respektuje i co robi z niespójnymi sygnałami w różnych grupach. Bez własnego pomiaru nie mamy tu nic do powiedzenia i nie zamierzamy powtarzać cudzych liczb. Poprawka, którą opisujemy, jest warta zrobienia niezależnie od tej wiedzy, bo naprawia plik, który mówi co innego, niż jego autor zamierzał, a to jest defekt sam w sobie.
Co z tym zrobić po stronie procesu
Sprawdzenie zasięgu sygnału jest jednorazową pracą na kilka minut, ale jego utrzymanie jednorazowe nie jest. Każda nowa grupa dopisana do pliku, a dopisuje się je regularnie, bo lista crawlerów AI rośnie z miesiąca na miesiąc, jest nową grupą bez sygnału, dopóki ktoś o nim nie pamięta. Dlatego druga z komend wyżej ma trafić do testów, a nie do notatki. Reguła, która sprawdza się sama, przeżyje; reguła zapisana w dokumencie przeżyje do następnej edycji pliku przez kogoś, kto tego dokumentu nie czytał.
Warto też pamiętać, o jakiej części ruchu tu mówimy. W naszym pomiarze z sierpnia 2026, opisanym we wpisie o ruchu botów na małej stronie, 72% żądań nie pochodziło z przeglądarki. Plik robots.txt jest jedynym miejscem, w którym w ogóle rozmawiamy z tą większością, i jedyną rzeczą, jaką ta większość o naszych warunkach wie. Jeśli zdanie w nim jest zaadresowane do złej grupy, to nie jest miękki błąd, tylko cisza.
Krótka lista kontrolna
- Policz grupy
User-agenti linieContent-Signalw swoim pliku. Jeśli liczby się nie zgadzają, różnica to grupy bez rezerwacji. - Powtórz linię w każdej grupie, której ma dotyczyć. Powtórzenie jest tu poprawną formą, nie duplikatem.
- Zdecyduj świadomie, czy każda grupa dostaje ten sam zestaw sygnałów. Dwadzieścia identycznych linii to zwykle znak, że nikt nie decydował.
- Sprawdź, czy masz osobne grupy dla ról tego samego dostawcy, na przykład dla pobrania inicjowanego przez użytkownika i dla zbierania korpusu.
- Wepnij sprawdzenie w pipeline. Lista crawlerów rośnie i każda dopisana grupa startuje bez sygnału.
- Nie opowiadaj sobie, że sygnał cokolwiek blokuje. Blokowanie to warstwa przed aplikacją, a to jest deklaracja.







