sc.raydata.co crawler audit — the GEO stack is built and the WAF 403s every crawler it was built for
The question
"Same crawler audit against sc.raydata.co — different Cloudflare Pages project, possibly different defaults, and the surface that should most aggressively want LLM citation."
Context: GEO Step 0 for the Sanity Check newsletter surface, mirroring the completed raydata.co check. Outstanding since the May 9 brief.
What we already know (from the vault)
- [[~/rdco-vault/06-reference/research/2026-05-18-raydata-co-crawler-status-9day-check.md|May 18 raydata.co 9-day delta check]] (read in full for this run) established the method: raydata.co serves a Cloudflare-managed robots.txt blocking ~10 AI crawlers, config frozen since 2026-04-24, and flagged running this same audit against
sc.raydata.coas an explicit open follow-up. - [[~/rdco-vault/06-reference/research/2026-05-09-raydata-co-bot-crawler-logs.md|May 9 raydata.co AI crawler status]] is the canonical empirical record. Per the parent brief's summary of it: GPTBot/PerplexityBot/Google-Extended/CCBot showed zero hits; ClaudeBot probed robots.txt and walked away; ChatGPT-User was reported as the only AI crawler reaching content pages. That last claim is directly contradicted by today's observation — see Contradictions.
- [[~/rdco-vault/06-reference/research/2026-04-22-publishing-for-agents-spec.md|Publishing for Agents spec]] proposed the llms.txt + canonical-markdown posture. Today's audit confirms that spec was actually implemented on sc.raydata.co — llms.txt and
.mdendpoints are live. - [[~/rdco-vault/02-sops/2026-04-26-rdco-web-app-deployment-standard.md|RDCO Web-App Deployment Standard]] names sc.raydata.co as the reference implementation of the standard deploy pattern.
- Policy decision teed up by the May 9 brief (keep blocked / search-but-not-train / open) remains unresolved. No config change has been applied.
What the web says
Empirical results observed 2026-07-16 ~05:04 UTC. All via curl against live endpoints.
- robots.txt exists and returns 200.
GET https://sc.raydata.co/robots.txt→HTTP/2 200,content-length: 1909,server: cloudflare. Identical body for default UA andUser-Agent: GPTBot. The operative section verbatim:
# BEGIN Cloudflare Managed content
User-agent: *
Content-Signal: search=yes,ai-train=no,use=reference
Allow: /
User-agent: Amazonbot
Disallow: /
User-agent: Applebot-Extended
Disallow: /
User-agent: Bytespider
Disallow: /
User-agent: CCBot
Disallow: /
User-agent: ClaudeBot
Disallow: /
User-agent: CloudflareBrowserRenderingCrawler
Disallow: /
User-agent: Google-Extended
Disallow: /
User-agent: GPTBot
Disallow: /
User-agent: meta-externalagent
Disallow: /
# END Cloudflare Managed Content
User-agent: *
Allow: /
Sitemap: https://sc.raydata.co/sitemap-index.xml
The Cloudflare managed block is byte-identical to raydata.co's.
diffof the two live robots.txt bodies returns exactly four added lines on sc.raydata.co — its own origin-authoredUser-agent: * / Allow: /plus theSitemap:pointer. Nothing else differs. Named-UA check across both files: GPTBot, ClaudeBot, Google-Extended, CCBot, Bytespider, Applebot-Extended each appear once in both;anthropic-ai,PerplexityBot,OAI-SearchBot,ChatGPT-User,Claude-User,Claude-SearchBotappear in neither.The WAF blocks far more than robots.txt declares — including every search-time citation agent. Per-UA probe of
https://sc.raydata.co/(homepage):
| User-agent | HTTP | Named in robots.txt? |
|---|---|---|
| control browser (Chrome) | 200 | — |
| Googlebot | 200 | no |
| Google-Extended | 200 | yes — Disallow: / |
| Applebot-Extended | 200 | yes — Disallow: / |
| GPTBot | 403 | yes |
| ClaudeBot | 403 | yes |
| CCBot | 403 | yes |
| Bytespider | 403 | yes |
| anthropic-ai | 403 | no |
| PerplexityBot | 403 | no |
| OAI-SearchBot | 403 | no |
| ChatGPT-User | 403 | no |
| Claude-User | 403 | no |
| Claude-SearchBot | 403 | no |
The 403 body is 25 bytes of text/plain: Your request was blocked. Headers on the block: cache-control: private, max-age=0, no-store..., x-frame-options: SAMEORIGIN, server: cloudflare. No cf-mitigated header and no challenge headers are present.
llms.txtis live, hand-written, explicitly invites AI citation — and 403s to GPTBot.GET /llms.txtreturns 200 to a browser. Verbatim excerpt: "License: All essays © Ben Wilson. Quoting with attribution and a link is welcome and encouraged." and "Every essay is also served as canonical markdown for AI tool consumption." The same URL asUser-Agent: GPTBot→ HTTP 403.The canonical markdown endpoints work and are unreachable to every AI agent.
GET /issues/analytics-canon.md→ control browser:HTTP=200 type=text/markdown; charset=utf-8 bytes=1463. Same URL as GPTBot / ChatGPT-User / Claude-User / PerplexityBot → 403 on all four.Sitemap present and correctly referenced.
/sitemap-index.xml→ 200 (containshttps://sc.raydata.co/sitemap-0.xml), and robots.txt points to it./sitemap.xml→ 404 (Astro convention; not an error)./sitemap-index.xmlas GPTBot → 403.No meta robots, noai, or X-Robots-Tag anywhere. Homepage HTML grep for
<meta ... robots|noai|noimageai>→ no matches. Homepage response headers contain noX-Robots-Tag. Full homepage headers:x-content-type-options: nosniff,access-control-allow-origin: *,cache-control: public, max-age=0, must-revalidate,referrer-policy: strict-origin-when-cross-origin,cf-cache-status: DYNAMIC. Every block observed is UA-based at the edge, not content-declared.The "different Pages project" premise is mechanically moot — same zone, same edge.
dig +short sc.raydata.co→104.21.35.56, 172.67.214.27.dig +short www.raydata.co→ the identical pair.dig +short NS raydata.co→frida.ns.cloudflare.com, gabe.ns.cloudflare.com. Both hostnames are subdomains inside one Cloudflare zone, so bot-management posture is inherited zone-wide regardless of which Pages project serves the origin.Industry context: Cloudflare is moving to defaults where, from September 15 2026, bots classified as Training or Agent are blocked on ad-displaying pages while Search stays allowed; the change applies to new customers, new sites for existing customers, and every existing free-tier customer, with an opt-out available before that date (Cloudflare bots docs, TechCrunch, The Register).
Convergences and contradictions
CONVERGENCE — the premise in the question is wrong, and that is the finding. sc.raydata.co does not have different defaults. The managed robots.txt block is byte-identical to raydata.co's and the per-UA WAF matrix is identical on both hosts (
www.raydata.coalso returns 200/200 for control+Googlebot and 403 for GPTBot, ClaudeBot, PerplexityBot, OAI-SearchBot, ChatGPT-User, Claude-User). Because both are subdomains of one Cloudflare zone resolving to the same anycast IPs, "different Pages project" never implied different crawler posture. One zone-level fix moves both surfaces at once — that is the good news buried in the bad.CONTRADICTION — the May 9 brief's "ChatGPT-User is the only AI crawler reaching content pages" does not hold today. ChatGPT-User now receives a 403 on both sc.raydata.co and www.raydata.co. Either the WAF posture tightened between May 9 and today, or the May 9 log-derived claim reflected traffic that predated a rule change. I did not re-query Cloudflare Analytics this run (no attempt made — the parent brief records the 1Password
CloudFlare - claude-rdcocredential as broken), so I cannot date the change. Flagged as an unresolved discrepancy, not corrected.CONTRADICTION — robots.txt and the WAF disagree with each other, in both directions. robots.txt says
Content-Signal: search=yesand does not disallow any search-time agent, yet the WAF 403s all of them. Meanwhile Google-Extended and Applebot-Extended areDisallow: /in robots.txt but sail through the WAF with 200. The declared policy and the enforced policy are two different policies. Also note the wildcard group isAllow: /, notDisallow: /— the parent brief's shorthand ("serves Cloudflare's managedDisallow: /robots.txt") is imprecise. Minor drift since May 18: the Content-Signal now readssearch=yes,ai-train=no,use=reference; the parent recordedsearch=yes,ai-train=nowith nousetoken, and thelast-modified: Fri, 24 Apr 2026 23:00:43 GMTheader the parent cited is absent from today's response entirely, so I cannot compute a modification delta.
Synthesis for RDCO
Sanity Check is blocking the exact crawlers it most wants citation from, and it is doing so at the WAF — one layer below the robots.txt everyone has been reading. This is the blunt version: someone built the full GEO stack on sc.raydata.co. There is a hand-written llms.txt that says "Quoting with attribution and a link is welcome and encouraged." There are canonical .md endpoints serving text/markdown for every issue. There is a sitemap-index, correctly referenced from robots.txt. The Publishing-for-Agents spec was not a proposal that died in the vault — it shipped. And every single one of those artifacts returns 403 to GPTBot, ChatGPT-User, Claude-User, Claude-SearchBot, OAI-SearchBot, and PerplexityBot. The invitation is written, published, and unreachable by its addressee. That is the whole finding.
The distinction that matters, and that the prior briefs blurred: robots.txt is not the binding constraint here — the WAF is. Every prior brief in this thread framed the problem as "Cloudflare's managed robots.txt disallows ten AI bots" and teed up a robots.txt policy decision. But robots.txt is a voluntary protocol, and the observable proof that it is not what's stopping anyone is sitting right in the matrix above: Google-Extended is Disallow: / in robots.txt and still gets a 200. It ignores the file. Conversely PerplexityBot, OAI-SearchBot, ChatGPT-User, Claude-User and Claude-SearchBot are named in no disallow group anywhere in that file and still get a 403. Rewriting robots.txt to "allow search but not training" — the May 9 brief's recommended path — would have changed nothing observable. The 403s would have continued. That recommendation would have been executed, verified against the wrong artifact, and declared a success while the surface stayed dark.
The critical asymmetry for a newsletter with zero ads and no paywall: the blocked set is precisely the citation path, not the training path. GPTBot and CCBot are training scrapers — blocking those is a defensible, deliberate stance, and ai-train=no says RDCO means it. But ChatGPT-User and Claude-User are user-initiated retrievals: a human asked a question, the assistant went to fetch this page for them, and RDCO returned "Your request was blocked." OAI-SearchBot, Claude-SearchBot and PerplexityBot are the index-builders behind AI search answers — the mechanism by which Sanity Check gets cited rather than consumed. Per the parent brief's own market data, PerplexityBot is the only AI crawler with a favorable crawl-to-referral ratio, and it is 403'd. Sanity Check is paying the full strategic cost of being invisible in AI answers while capturing none of the benefit of blocking training, because the training crawlers that actually respect robots.txt would have honored ai-train=no for free.
The fix, and its shape. This is one zone-level change on raydata.co, not two per-project changes — the DNS evidence settles that, and it means fixing Sanity Check fixes the umbrella site in the same action. The move is: disable the WAF-level AI-bot block (or scope it to the training UAs only), keep the managed robots.txt ai-train=no signal, and add an explicit allow for OAI-SearchBot, ChatGPT-User, Claude-User, Claude-SearchBot, PerplexityBot, Googlebot and Bingbot. Two hard caveats before anyone touches a dashboard. First, I cannot see the dashboard from outside and did not try — the plain 25-byte Your request was blocked. with no cf-mitigated header is consistent with a WAF custom rule with a custom response, and is not the signature I would expect from the managed "Block AI bots" toggle. Whether this is ai_bots_protection, a Bot Fight Mode side-effect, or a hand-written custom rule is UNVERIFIED; the confirming check is Security → WAF → Custom rules plus Security → Bots on the raydata.co zone, which needs the broken 1Password credential restored first. Do not assume the parent brief's ai_bots_protection attribution is right — it was written before this evidence existed. Second, the September 15 2026 Cloudflare default shift lands on free-tier zones automatically; if raydata.co is free-tier, posture may move again on its own, and any fix applied now should be re-verified after that date with this exact UA matrix. Until the credential is restored, the honest verdict on why the 403 fires is unknown — but the 403 itself is not in doubt, and neither is what it costs.
Why this is in the vault
This is the empirical Step 0 that unblocks (or kills) the GEO bet for Sanity Check, and it materially reframes the decision the May 9 and May 18 briefs teed up: the pending founder decision was scoped as a robots.txt policy question, and this audit shows robots.txt is not the binding layer — so executing the previously-recommended option 2 would have produced zero observable change. It also converts the raydata.co-vs-sc.raydata.co question from "audit two surfaces" into "one zone-level fix," which changes the cost estimate on the fix itself.
Open follow-ups
- [DECISION owed by founder] The policy question is unchanged but now correctly scoped: block training only (
ai-train=no+ WAF scoped to GPTBot/CCBot/Bytespider), or keep everything blocked? The status quo blocks citation while gaining nothing on training. Note the prior recommendation (rewrite robots.txt) is now known to be a no-op. - [BLOCKER, precedes any fix] Restore the 1Password
Ray Agent→CloudFlare - claude-rdcoitem. Every remaining question — which rule fires, whether the zone is free-tier, what Analytics shows — is gated on it. Outstanding since May 18. - Determine from the dashboard which mechanism produces the 403: WAF custom rule vs
ai_bots_protectionvs Bot Fight Mode. The parent brief's attribution is unverified and the response signature argues against it. - Date the ChatGPT-User posture change. May 9 logged it reaching content; today it 403s. Cloudflare Analytics or WAF rule-change history would settle whether the WAF tightened after May 9.
- Is
raydata.coon a free Cloudflare plan? Determines whether the Sept 15 2026 default shift applies automatically and whether an opt-out is needed before then. - Re-run this exact UA matrix after any config change, and again after 2026-09-15. It is a cheap, scriptable regression test — candidate for a cron'd check rather than a research question.
- Does Sanity Check's Substack→Ghost migration ([[~/rdco-vault/06-reference/research/2026-07-04-substack-ghost-permalink-redirect-plan.md|permalink redirect plan]]) change the origin/zone assumptions here? If the surface moves, re-audit.
Related
- [[~/rdco-vault/06-reference/research/2026-05-18-raydata-co-crawler-status-9day-check.md|raydata.co AI crawler status — 9-day delta check]] — the parent brief this mirrors
- [[~/rdco-vault/06-reference/research/2026-05-09-raydata-co-bot-crawler-logs.md|raydata.co AI crawler status — empirical brief]] — canonical record; contains the ChatGPT-User claim contradicted here
- [[~/rdco-vault/06-reference/research/2026-04-22-publishing-for-agents-spec.md|Publishing for Agents — Spec for raydata.co]] — the llms.txt/markdown posture confirmed live on sc.raydata.co
- [[~/rdco-vault/06-reference/research/2026-04-22-agent-seo-state-of-the-discipline.md|Agent SEO — state of the discipline]] — the GEO thesis motivating the audit
- [[~/rdco-vault/02-sops/2026-04-26-rdco-web-app-deployment-standard.md|RDCO Web-App Deployment Standard]] — sc.raydata.co deploy topology
- [[~/rdco-vault/06-reference/research/2026-07-04-substack-ghost-permalink-redirect-plan.md|Substack → Ghost permalink redirect plan]] — pending migration affecting this surface
- [[~/rdco-vault/01-projects/sanity-check/v3-positioning-2026-05-08.md|Sanity Check v3 — Positioning Document]] — why this surface wants citation
Sources
Empirical (primary, all run 2026-07-16 ~05:04–05:06 UTC):
curl -sS -i https://sc.raydata.co/robots.txt(default UA andUser-Agent: GPTBot) — 200, 1909 bytesdiffof livehttps://sc.raydata.co/robots.txtvshttps://www.raydata.co/robots.txt— 4-line delta- Per-UA probe matrix,
https://sc.raydata.co/andhttps://www.raydata.co/— 14 and 8 user-agents respectively curl https://sc.raydata.co/llms.txt,/sitemap-index.xml,/sitemap.xml,/issues/analytics-canon.md— control UA and AI UAsdig +short sc.raydata.co,dig +short www.raydata.co,dig +short NS raydata.co- Homepage HTML grep for meta robots/noai; response-header inspection for X-Robots-Tag / cf-mitigated
Vault:
- [[~/rdco-vault/06-reference/research/2026-05-18-raydata-co-crawler-status-9day-check.md]] (read in full)
- [[~/rdco-vault/06-reference/research/2026-05-09-raydata-co-bot-crawler-logs.md]] (surfaced via QMD; not re-read this run — claims about it are relayed from the parent brief's summary)
- [[
/rdco-vault/06-reference/research/2026-04-22-publishing-for-agents-spec.md]], [[/rdco-vault/02-sops/2026-04-26-rdco-web-app-deployment-standard.md]], [[/rdco-vault/01-projects/sanity-check/v3-positioning-2026-05-08.md]], [[/rdco-vault/06-reference/research/2026-07-04-substack-ghost-permalink-redirect-plan.md]] (surfaced via QMD; titles/snippets only)
Web: