[{"data":1,"prerenderedAt":2117},["ShallowReactive",2],{"docs-navigation":3,"docs-\u002Fdocs\u002Fcrawler-logs\u002Fcloudflare-worker":213},[4],{"title":5,"path":6,"stem":7,"children":8,"page":30},"Docs","\u002Fdocs","docs",[9,31,56,98,118,138,163],{"title":10,"description":11,"group":11,"path":12,"stem":13,"children":14,"page":30},"Getting started",null,"\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",[15,20,25],{"title":16,"path":17,"stem":18,"description":19,"group":11},"Overview","\u002Fdocs\u002Fgetting-started\u002Foverview","docs\u002F1.getting-started\u002F1.overview","What Shruwd does and what you get out of it.",{"title":21,"path":22,"stem":23,"description":24,"group":11},"Quickstart","\u002Fdocs\u002Fgetting-started\u002Fquickstart","docs\u002F1.getting-started\u002F2.quickstart","From an empty account to your first measurement.",{"title":26,"path":27,"stem":28,"description":29,"group":11},"Reading your results","\u002Fdocs\u002Fgetting-started\u002Freading-your-results","docs\u002F1.getting-started\u002F3.reading-your-results","What the four numbers mean, and when to trust them.",false,{"title":32,"description":11,"group":11,"path":33,"stem":34,"children":35,"page":30},"What you track","\u002Fdocs\u002Ftracking","docs\u002F2.tracking",[36,41,46,51],{"title":37,"path":38,"stem":39,"description":40,"group":11},"Brands","\u002Fdocs\u002Ftracking\u002Fbrands","docs\u002F2.tracking\u002F1.brands","Add, rename and archive the brands you measure.",{"title":42,"path":43,"stem":44,"description":45,"group":11},"Prompts","\u002Fdocs\u002Ftracking\u002Fprompts","docs\u002F2.tracking\u002F2.prompts","Choose and manage the questions Shruwd asks each engine.",{"title":47,"path":48,"stem":49,"description":50,"group":11},"Competitors","\u002Fdocs\u002Ftracking\u002Fcompetitors","docs\u002F2.tracking\u002F3.competitors","Track the brands you are compared against, and how they are matched.",{"title":52,"path":53,"stem":54,"description":55,"group":11},"Connecting Search Console","\u002Fdocs\u002Ftracking\u002Fconnecting-search-console","docs\u002F2.tracking\u002F4.connecting-search-console","Add per-page search demand so two diagnostics can run.",{"title":57,"description":11,"group":11,"path":58,"stem":59,"children":60,"page":30},"Crawler logs","\u002Fdocs\u002Fcrawler-logs","docs\u002F3.crawler-logs",[61,66,72,77,82,87,93],{"title":62,"path":63,"stem":64,"description":65,"group":11},"Choosing a log path","\u002Fdocs\u002Fcrawler-logs\u002Fchoosing-a-log-path","docs\u002F3.crawler-logs\u002F1.choosing-a-log-path","Pick the way of sending crawler logs that matches your hosting.",{"title":67,"path":68,"stem":69,"description":70,"group":71},"Platform log drains","\u002Fdocs\u002Fcrawler-logs\u002Fplatform-log-drains","docs\u002F3.crawler-logs\u002F2.platform-log-drains","Send logs from Vercel, Netlify, Fly and similar. No code.","Setup guides",{"title":73,"path":74,"stem":75,"description":76,"group":71},"Cloudflare Worker","\u002Fdocs\u002Fcrawler-logs\u002Fcloudflare-worker","docs\u002F3.crawler-logs\u002F3.cloudflare-worker","Forward crawler hits from behind Cloudflare, on any plan.",{"title":78,"path":79,"stem":80,"description":81,"group":71},"Cloudflare Logpush","\u002Fdocs\u002Fcrawler-logs\u002Fcloudflare-logpush","docs\u002F3.crawler-logs\u002F4.cloudflare-logpush","Send zone request logs straight to Shruwd. Enterprise zones, no code.",{"title":83,"path":84,"stem":85,"description":86,"group":71},"Server middleware","\u002Fdocs\u002Fcrawler-logs\u002Fserver-middleware","docs\u002F3.crawler-logs\u002F5.server-middleware","Forward crawler hits from a Nuxt or Nitro origin. Last resort.",{"title":88,"path":89,"stem":90,"description":91,"group":92},"Crawlers tracked","\u002Fdocs\u002Fcrawler-logs\u002Fcrawlers-tracked","docs\u002F3.crawler-logs\u002F6.crawlers-tracked","Which bots Shruwd recognises, and how a hit is verified.","Reference",{"title":94,"path":95,"stem":96,"description":97,"group":92},"Ingest not arriving","\u002Fdocs\u002Fcrawler-logs\u002Fingest-not-arriving","docs\u002F3.crawler-logs\u002F7.ingest-not-arriving","Diagnose a log drain that is not delivering.",{"title":99,"description":11,"group":11,"path":100,"stem":101,"children":102,"page":30},"Findings and fixes","\u002Fdocs\u002Ffindings","docs\u002F4.findings",[103,108,113],{"title":104,"path":105,"stem":106,"description":107,"group":11},"How findings work","\u002Fdocs\u002Ffindings\u002Fhow-findings-work","docs\u002F4.findings\u002F1.how-findings-work","What a finding is, how much to trust it, and how to prove your fix worked.",{"title":109,"path":110,"stem":111,"description":112,"group":11},"Rechecks","\u002Fdocs\u002Ffindings\u002Frechecks","docs\u002F4.findings\u002F2.rechecks","How Shruwd proves whether your fix moved the number.",{"title":114,"path":115,"stem":116,"description":117,"group":11},"Findings reference","\u002Fdocs\u002Ffindings\u002Ffindings-reference","docs\u002F4.findings\u002F3.findings-reference","Every diagnostic Shruwd runs, what triggers it and what to do.",{"title":119,"description":11,"group":11,"path":120,"stem":121,"children":122,"page":30},"Metrics","\u002Fdocs\u002Fmetrics","docs\u002F5.metrics",[123,128,133],{"title":124,"path":125,"stem":126,"description":127,"group":11},"Metric definitions","\u002Fdocs\u002Fmetrics\u002Fmetric-definitions","docs\u002F5.metrics\u002F1.metric-definitions","Exactly what each number counts, and when it is undefined.",{"title":129,"path":130,"stem":131,"description":132,"group":11},"Confidence and sample size","\u002Fdocs\u002Fmetrics\u002Fconfidence-and-sample-size","docs\u002F5.metrics\u002F2.confidence-and-sample-size","Why every number has a range, and why some are withheld.",{"title":134,"path":135,"stem":136,"description":137,"group":11},"When a number has moved","\u002Fdocs\u002Fmetrics\u002Fwhen-a-number-has-moved","docs\u002F5.metrics\u002F3.when-a-number-has-moved","The rule Shruwd uses before calling a change real.",{"title":139,"description":11,"group":11,"path":140,"stem":141,"children":142,"page":30},"Account","\u002Fdocs\u002Faccount","docs\u002F6.account",[143,148,153,158],{"title":144,"path":145,"stem":146,"description":147,"group":11},"Plans and limits","\u002Fdocs\u002Faccount\u002Fplans-and-limits","docs\u002F6.account\u002F1.plans-and-limits","What each plan includes and what happens when you reach a limit.",{"title":149,"path":150,"stem":151,"description":152,"group":11},"Team and roles","\u002Fdocs\u002Faccount\u002Fteam-and-roles","docs\u002F6.account\u002F2.team-and-roles","Invite people, set what they can change, and remove access.",{"title":154,"path":155,"stem":156,"description":157,"group":11},"Billing","\u002Fdocs\u002Faccount\u002Fbilling","docs\u002F6.account\u002F3.billing","Change plan, read your usage, and handle a failed payment.",{"title":159,"path":160,"stem":161,"description":162,"group":11},"Email notifications","\u002Fdocs\u002Faccount\u002Femail-notifications","docs\u002F6.account\u002F4.email-notifications","What Shruwd emails you, and how to turn it off.",{"title":164,"description":11,"group":11,"path":165,"stem":166,"children":167,"page":30},"Build on Shruwd","\u002Fdocs\u002Fapi","docs\u002F7.api",[168,173,178,182,187,192,197,203,208],{"title":169,"path":170,"stem":171,"description":172,"group":11},"API overview","\u002Fdocs\u002Fapi\u002Fapi-overview","docs\u002F7.api\u002F1.api-overview","Authentication, errors, limits and the rules every endpoint follows.",{"title":37,"path":174,"stem":175,"description":176,"group":177},"\u002Fdocs\u002Fapi\u002Fbrands","docs\u002F7.api\u002F2.brands","Create, read, rename and archive brands over the API.","Endpoints",{"title":42,"path":179,"stem":180,"description":181,"group":177},"\u002Fdocs\u002Fapi\u002Fprompts","docs\u002F7.api\u002F3.prompts","Add, edit and retire the questions asked every cycle.",{"title":183,"path":184,"stem":185,"description":186,"group":177},"Entities","\u002Fdocs\u002Fapi\u002Fentities","docs\u002F7.api\u002F4.entities","Manage the brand and its competitors, and the aliases that count.",{"title":188,"path":189,"stem":190,"description":191,"group":177},"Measurement","\u002Fdocs\u002Fapi\u002Fmeasurement","docs\u002F7.api\u002F5.measurement","Run cycles and read visibility, crawlers and individual answers.",{"title":193,"path":194,"stem":195,"description":196,"group":177},"Findings","\u002Fdocs\u002Fapi\u002Ffindings","docs\u002F7.api\u002F6.findings","Read diagnosed findings and move them through their lifecycle.",{"title":198,"path":199,"stem":200,"description":201,"group":202},"TypeScript SDK","\u002Fdocs\u002Fapi\u002Ftypescript-sdk","docs\u002F7.api\u002F7.typescript-sdk","A typed client for the Shruwd API.","Clients and agents",{"title":204,"path":205,"stem":206,"description":207,"group":202},"MCP server","\u002Fdocs\u002Fapi\u002Fmcp-server","docs\u002F7.api\u002F8.mcp-server","Give Claude or Cursor tools to run Shruwd.",{"title":209,"path":210,"stem":211,"description":212,"group":202},"For AI agents","\u002Fdocs\u002Fapi\u002Ffor-ai-agents","docs\u002F7.api\u002F9.for-ai-agents","Machine-readable versions of everything here.",{"id":214,"title":73,"body":215,"description":2111,"extension":2112,"group":71,"meta":2113,"navigation":2114,"path":74,"section":57,"seo":2115,"stem":75,"__hash__":2116},"docs\u002Fdocs\u002F3.crawler-logs\u002F3.cloudflare-worker.md",{"type":216,"value":217,"toc":2102},"minimark",[218,222,231,234,253,258,282,286,312,317,321,324,466,1862,1866,1872,1943,1950,1954,1957,1990,1993,1997,2004,2058,2072,2082,2086,2098],[219,220,73],"h1",{"id":221},"cloudflare-worker",[223,224,225,226,230],"p",{},"A small Worker on a route in front of your site forwards AI-crawler hits to Shruwd. It\nruns ",[227,228,229],"strong",{},"before cache lookup",", so it sees the requests Cloudflare answers from cache\nwithout calling your origin — which on a static or prerendered site is most of the pages\nworth measuring.",[223,232,233],{},"Works on any Cloudflare plan. It sends one short line per crawler hit and nothing at all\nfor ordinary visitors.",[235,236,238,250],"callout",{"type":237},"warning",[223,239,240,241,244,245,249],{},"The Worker only sees requests Cloudflare lets through. A request refused by Cloudflare's\nown settings (AI crawler blocks, WAF custom rules, rate limiting) stops before the Worker\nruns, so Shruwd never hears about it. Cloudflare does not document where Bot Fight Mode\nruns, so do not count on seeing its refusals either. To see what Cloudflare refused, open\n",[227,242,243],{},"Security → Analytics"," in the Cloudflare dashboard. On an Enterprise zone,\n",[246,247,248],"a",{"href":79},"Logpush"," records those refusals too.",[223,251,252],{},"What the Worker does see is a refusal made behind Cloudflare — by your host, a security\nplugin or your server — and the finding says that is where it happened.",[254,255,257],"h2",{"id":256},"prerequisites","Prerequisites",[259,260,261,268,271],"ul",{},[262,263,264,267],"li",{},[227,265,266],{},"Permissions:"," admin or owner, to create the token.",[262,269,270],{},"A Cloudflare zone for your domain, and permission to deploy a Worker on it.",[262,272,273,277,278,281],{},[274,275,276],"code",{},"wrangler"," available (",[274,279,280],{},"npx wrangler"," needs no install).",[254,283,285],{"id":284},"_1-get-your-endpoint-and-token","1. Get your endpoint and token",[287,288,289,296,302],"ol",{},[262,290,291,292,295],{},"Open the brand's ",[227,293,294],{},"Settings → Crawler logs",".",[262,297,298,299,295],{},"Select ",[227,300,301],{},"Create token",[262,303,304,305,308,309,295],{},"Copy the ",[227,306,307],{},"endpoint"," and the ",[227,310,311],{},"token",[235,313,314],{"type":237},[223,315,316],{},"The token is shown once and cannot be retrieved later. Creating a new one immediately\nrevokes the old one, which will stop a Worker that is already running.",[254,318,320],{"id":319},"_2-create-the-worker","2. Create the Worker",[223,322,323],{},"Make a directory in your site's repository with the two files below.",[325,326,332],"pre",{"className":327,"code":328,"filename":329,"language":330,"meta":331,"style":331},"language-jsonc shiki shiki-themes github-light github-dark","{\n  \"$schema\": \"node_modules\u002Fwrangler\u002Fconfig-schema.json\",\n  \"name\": \"shruwd-log-forwarder\",\n  \"main\": \"src\u002Findex.js\",\n  \"compatibility_date\": \"2026-09-01\",\n\n  \u002F\u002F The route must match your canonical host ONLY. If customer custom domains\n  \u002F\u002F resolve through this same zone, this pattern must not catch them.\n  \"routes\": [\n    { \"pattern\": \"example.com\u002F*\", \"zone_name\": \"example.com\" }\n  ],\n\n  \"vars\": {\n    \"CANONICAL_HOST\": \"example.com\",\n\n    \u002F\u002F Comma-separated path prefixes. \"\u002F\" is the root page only;\n    \u002F\u002F \"\u002Fblog\u002F\" is everything underneath it.\n    \"PATH_ALLOWLIST\": \"\u002F,\u002Fpricing,\u002Ffeatures,\u002Fblog\u002F,\u002Fdocs\u002F\",\n\n    \"SHRUWD_INGEST_URL\": \"YOUR_ENDPOINT\"\n  }\n}\n","wrangler.jsonc","jsonc","",[274,333,334,342,348,354,360,366,373,379,385,391,397,403,408,414,420,425,431,437,443,448,454,460],{"__ignoreMap":331},[335,336,339],"span",{"class":337,"line":338},"line",1,[335,340,341],{},"{\n",[335,343,345],{"class":337,"line":344},2,[335,346,347],{},"  \"$schema\": \"node_modules\u002Fwrangler\u002Fconfig-schema.json\",\n",[335,349,351],{"class":337,"line":350},3,[335,352,353],{},"  \"name\": \"shruwd-log-forwarder\",\n",[335,355,357],{"class":337,"line":356},4,[335,358,359],{},"  \"main\": \"src\u002Findex.js\",\n",[335,361,363],{"class":337,"line":362},5,[335,364,365],{},"  \"compatibility_date\": \"2026-09-01\",\n",[335,367,369],{"class":337,"line":368},6,[335,370,372],{"emptyLinePlaceholder":371},true,"\n",[335,374,376],{"class":337,"line":375},7,[335,377,378],{},"  \u002F\u002F The route must match your canonical host ONLY. If customer custom domains\n",[335,380,382],{"class":337,"line":381},8,[335,383,384],{},"  \u002F\u002F resolve through this same zone, this pattern must not catch them.\n",[335,386,388],{"class":337,"line":387},9,[335,389,390],{},"  \"routes\": [\n",[335,392,394],{"class":337,"line":393},10,[335,395,396],{},"    { \"pattern\": \"example.com\u002F*\", \"zone_name\": \"example.com\" }\n",[335,398,400],{"class":337,"line":399},11,[335,401,402],{},"  ],\n",[335,404,406],{"class":337,"line":405},12,[335,407,372],{"emptyLinePlaceholder":371},[335,409,411],{"class":337,"line":410},13,[335,412,413],{},"  \"vars\": {\n",[335,415,417],{"class":337,"line":416},14,[335,418,419],{},"    \"CANONICAL_HOST\": \"example.com\",\n",[335,421,423],{"class":337,"line":422},15,[335,424,372],{"emptyLinePlaceholder":371},[335,426,428],{"class":337,"line":427},16,[335,429,430],{},"    \u002F\u002F Comma-separated path prefixes. \"\u002F\" is the root page only;\n",[335,432,434],{"class":337,"line":433},17,[335,435,436],{},"    \u002F\u002F \"\u002Fblog\u002F\" is everything underneath it.\n",[335,438,440],{"class":337,"line":439},18,[335,441,442],{},"    \"PATH_ALLOWLIST\": \"\u002F,\u002Fpricing,\u002Ffeatures,\u002Fblog\u002F,\u002Fdocs\u002F\",\n",[335,444,446],{"class":337,"line":445},19,[335,447,372],{"emptyLinePlaceholder":371},[335,449,451],{"class":337,"line":450},20,[335,452,453],{},"    \"SHRUWD_INGEST_URL\": \"YOUR_ENDPOINT\"\n",[335,455,457],{"class":337,"line":456},21,[335,458,459],{},"  }\n",[335,461,463],{"class":337,"line":462},22,[335,464,465],{},"}\n",[325,467,472],{"className":468,"code":469,"filename":470,"language":471,"meta":331,"style":331},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F Forwards AI-crawler hits to Shruwd. Fails open: whatever happens in here,\n\u002F\u002F your site's response is returned unchanged.\n\n\u002F\u002F A broad prefilter, not the bot list. Identification proper happens on\n\u002F\u002F Shruwd's side against a catalogue that is kept current, so this only has to\n\u002F\u002F be loose enough not to drop something that catalogue would have caught.\nconst BOT_UA_PREFILTER =\n  \u002Fbot|crawl|spider|gpt|claude|perplexity|bytespider|applebot|ccbot|meta-external|google|extended\u002Fi\n\n\u002F\u002F Static assets are never the page a crawler judges you on. .txt and .xml stay,\n\u002F\u002F because robots.txt and sitemaps are interesting.\nconst ASSET_PATH =\n  \u002F\\.(?:js|mjs|cjs|css|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|mp3|wav|pdf|zip|gz|json|wasm)$\u002Fi\n\nfunction parseAllowlist(value) {\n  return String(value ?? '\u002F').split(',').map((s) => s.trim()).filter(Boolean)\n}\n\n\u002F\u002F Cheapest checks first — this runs on every request to the zone.\nfunction decide({ method, host, path, userAgent }, { canonicalHost, allowlist }) {\n  if (method !== 'GET' && method !== 'HEAD') return false\n  if (host.toLowerCase() !== canonicalHost.toLowerCase()) return false\n  if (ASSET_PATH.test(path)) return false\n  if (!allowlist.some((p) => (p === '\u002F' ? path === '\u002F' : path === p || path.startsWith(p)))) return false\n  return BOT_UA_PREFILTER.test(userAgent)\n}\n\nlet warned = false\n\nfunction maybeReport(request, response, env, ctx) {\n  const url = new URL(request.url)\n  const userAgent = request.headers.get('user-agent') ?? ''\n\n  const report = decide(\n    { method: request.method, host: url.hostname, path: url.pathname, userAgent },\n    { canonicalHost: env.CANONICAL_HOST, allowlist: parseAllowlist(env.PATH_ALLOWLIST) },\n  )\n  if (!report) return\n\n  \u002F\u002F Set by the edge; a client cannot forge it. Inside a Worker,\n  \u002F\u002F x-forwarded-for can be forged, so it is not used as a fallback.\n  const clientIp = request.headers.get('cf-connecting-ip')\n  if (!clientIp) return\n\n  \u002F\u002F The query string is never sent: it carries ids on some routes, and a\n  \u002F\u002F per-page crawl count needs none of it.\n  const line = {\n    timestamp: new Date().toISOString(),\n    host: url.hostname,\n    path: url.pathname,\n    user_agent: userAgent,\n    status: response.status,\n    client_ip: clientIp,\n    method: request.method,\n    bytes: Number(response.headers.get('content-length') ?? 0) || 0,\n  }\n\n  \u002F\u002F One request per hit, deliberately. A Worker isolate is ephemeral and\n  \u002F\u002F per-colo, so a buffer would be lost on eviction and split across colos.\n  \u002F\u002F Volume is low because the prefilter already ran.\n  ctx.waitUntil(\n    fetch(env.SHRUWD_INGEST_URL, {\n      method: 'POST',\n      headers: {\n        'x-shruwd-ingest-token': env.SHRUWD_INGEST_TOKEN,\n        'content-type': 'application\u002Fx-ndjson',\n      },\n      body: JSON.stringify(line) + '\\n',\n    })\n      .then((res) => {\n        if (!res.ok && !warned) {\n          warned = true\n          console.warn(`shruwd forwarder: ingest answered ${res.status}; check the token and URL`)\n        }\n      })\n      .catch((error) => {\n        if (!warned) {\n          warned = true\n          console.warn('shruwd forwarder: ingest unreachable', error?.message ?? error)\n        }\n      }),\n  )\n}\n\nexport default {\n  async fetch(request, env, ctx) {\n    \u002F\u002F Never inside the try. The only unconditional path is returning your\n    \u002F\u002F origin's response — a logging pipeline that can break the site it\n    \u002F\u002F observes is worse than one that loses events.\n    const response = await fetch(request)\n    try {\n      maybeReport(request, response, env, ctx)\n    } catch {\n      \u002F\u002F deliberately silent\n    }\n    return response\n  },\n}\n","src\u002Findex.js","js",[274,473,474,480,485,489,494,499,504,517,589,593,598,603,612,757,761,781,840,844,848,853,896,929,956,979,1047,1061,1066,1071,1085,1090,1120,1141,1169,1174,1189,1195,1219,1225,1240,1245,1251,1257,1279,1293,1298,1304,1310,1323,1344,1350,1356,1362,1368,1374,1380,1415,1420,1425,1431,1437,1443,1454,1468,1479,1485,1499,1513,1519,1550,1556,1576,1598,1609,1635,1641,1647,1666,1677,1686,1706,1711,1717,1722,1727,1732,1743,1766,1772,1778,1784,1803,1811,1820,1830,1836,1842,1851,1857],{"__ignoreMap":331},[335,475,476],{"class":337,"line":338},[335,477,479],{"class":478},"sJ8bj","\u002F\u002F Forwards AI-crawler hits to Shruwd. Fails open: whatever happens in here,\n",[335,481,482],{"class":337,"line":344},[335,483,484],{"class":478},"\u002F\u002F your site's response is returned unchanged.\n",[335,486,487],{"class":337,"line":350},[335,488,372],{"emptyLinePlaceholder":371},[335,490,491],{"class":337,"line":356},[335,492,493],{"class":478},"\u002F\u002F A broad prefilter, not the bot list. Identification proper happens on\n",[335,495,496],{"class":337,"line":362},[335,497,498],{"class":478},"\u002F\u002F Shruwd's side against a catalogue that is kept current, so this only has to\n",[335,500,501],{"class":337,"line":368},[335,502,503],{"class":478},"\u002F\u002F be loose enough not to drop something that catalogue would have caught.\n",[335,505,506,510,514],{"class":337,"line":375},[335,507,509],{"class":508},"szBVR","const",[335,511,513],{"class":512},"sj4cs"," BOT_UA_PREFILTER",[335,515,516],{"class":508}," =\n",[335,518,519,523,527,530,533,535,538,540,543,545,548,550,553,555,558,560,563,565,568,570,573,575,578,580,583,586],{"class":337,"line":381},[335,520,522],{"class":521},"sZZnC","  \u002F",[335,524,526],{"class":525},"sA_wV","bot",[335,528,529],{"class":508},"|",[335,531,532],{"class":525},"crawl",[335,534,529],{"class":508},[335,536,537],{"class":525},"spider",[335,539,529],{"class":508},[335,541,542],{"class":525},"gpt",[335,544,529],{"class":508},[335,546,547],{"class":525},"claude",[335,549,529],{"class":508},[335,551,552],{"class":525},"perplexity",[335,554,529],{"class":508},[335,556,557],{"class":525},"bytespider",[335,559,529],{"class":508},[335,561,562],{"class":525},"applebot",[335,564,529],{"class":508},[335,566,567],{"class":525},"ccbot",[335,569,529],{"class":508},[335,571,572],{"class":525},"meta-external",[335,574,529],{"class":508},[335,576,577],{"class":525},"google",[335,579,529],{"class":508},[335,581,582],{"class":525},"extended",[335,584,585],{"class":521},"\u002F",[335,587,588],{"class":508},"i\n",[335,590,591],{"class":337,"line":387},[335,592,372],{"emptyLinePlaceholder":371},[335,594,595],{"class":337,"line":393},[335,596,597],{"class":478},"\u002F\u002F Static assets are never the page a crawler judges you on. .txt and .xml stay,\n",[335,599,600],{"class":337,"line":399},[335,601,602],{"class":478},"\u002F\u002F because robots.txt and sitemaps are interesting.\n",[335,604,605,607,610],{"class":337,"line":405},[335,606,509],{"class":508},[335,608,609],{"class":512}," ASSET_PATH",[335,611,516],{"class":508},[335,613,614,616,620,623,625,628,630,633,635,638,640,643,645,648,650,653,656,659,661,664,666,669,671,674,676,679,681,684,686,689,692,695,697,700,702,705,707,710,712,715,717,720,722,725,727,730,732,735,737,740,742,745,747,750,753,755],{"class":337,"line":410},[335,615,522],{"class":521},[335,617,619],{"class":618},"snhLl","\\.",[335,621,622],{"class":525},"(?:js",[335,624,529],{"class":508},[335,626,627],{"class":525},"mjs",[335,629,529],{"class":508},[335,631,632],{"class":525},"cjs",[335,634,529],{"class":508},[335,636,637],{"class":525},"css",[335,639,529],{"class":508},[335,641,642],{"class":525},"map",[335,644,529],{"class":508},[335,646,647],{"class":525},"png",[335,649,529],{"class":508},[335,651,652],{"class":525},"jpe",[335,654,655],{"class":508},"?",[335,657,658],{"class":525},"g",[335,660,529],{"class":508},[335,662,663],{"class":525},"gif",[335,665,529],{"class":508},[335,667,668],{"class":525},"webp",[335,670,529],{"class":508},[335,672,673],{"class":525},"avif",[335,675,529],{"class":508},[335,677,678],{"class":525},"svg",[335,680,529],{"class":508},[335,682,683],{"class":525},"ico",[335,685,529],{"class":508},[335,687,688],{"class":525},"woff2",[335,690,691],{"class":508},"?|",[335,693,694],{"class":525},"ttf",[335,696,529],{"class":508},[335,698,699],{"class":525},"otf",[335,701,529],{"class":508},[335,703,704],{"class":525},"eot",[335,706,529],{"class":508},[335,708,709],{"class":525},"mp4",[335,711,529],{"class":508},[335,713,714],{"class":525},"webm",[335,716,529],{"class":508},[335,718,719],{"class":525},"mp3",[335,721,529],{"class":508},[335,723,724],{"class":525},"wav",[335,726,529],{"class":508},[335,728,729],{"class":525},"pdf",[335,731,529],{"class":508},[335,733,734],{"class":525},"zip",[335,736,529],{"class":508},[335,738,739],{"class":525},"gz",[335,741,529],{"class":508},[335,743,744],{"class":525},"json",[335,746,529],{"class":508},[335,748,749],{"class":525},"wasm)",[335,751,752],{"class":508},"$",[335,754,585],{"class":521},[335,756,588],{"class":508},[335,758,759],{"class":337,"line":416},[335,760,372],{"emptyLinePlaceholder":371},[335,762,763,766,770,774,778],{"class":337,"line":422},[335,764,765],{"class":508},"function",[335,767,769],{"class":768},"sScJk"," parseAllowlist",[335,771,773],{"class":772},"sVt8B","(",[335,775,777],{"class":776},"s4XuR","value",[335,779,780],{"class":772},") {\n",[335,782,783,786,789,792,795,798,801,804,806,809,811,813,816,819,822,825,828,831,834,837],{"class":337,"line":427},[335,784,785],{"class":508},"  return",[335,787,788],{"class":768}," String",[335,790,791],{"class":772},"(value ",[335,793,794],{"class":508},"??",[335,796,797],{"class":521}," '\u002F'",[335,799,800],{"class":772},").",[335,802,803],{"class":768},"split",[335,805,773],{"class":772},[335,807,808],{"class":521},"','",[335,810,800],{"class":772},[335,812,642],{"class":768},[335,814,815],{"class":772},"((",[335,817,818],{"class":776},"s",[335,820,821],{"class":772},") ",[335,823,824],{"class":508},"=>",[335,826,827],{"class":772}," s.",[335,829,830],{"class":768},"trim",[335,832,833],{"class":772},"()).",[335,835,836],{"class":768},"filter",[335,838,839],{"class":772},"(Boolean)\n",[335,841,842],{"class":337,"line":433},[335,843,465],{"class":772},[335,845,846],{"class":337,"line":439},[335,847,372],{"emptyLinePlaceholder":371},[335,849,850],{"class":337,"line":445},[335,851,852],{"class":478},"\u002F\u002F Cheapest checks first — this runs on every request to the zone.\n",[335,854,855,857,860,863,866,869,872,874,877,879,882,885,888,890,893],{"class":337,"line":450},[335,856,765],{"class":508},[335,858,859],{"class":768}," decide",[335,861,862],{"class":772},"({ ",[335,864,865],{"class":776},"method",[335,867,868],{"class":772},", ",[335,870,871],{"class":776},"host",[335,873,868],{"class":772},[335,875,876],{"class":776},"path",[335,878,868],{"class":772},[335,880,881],{"class":776},"userAgent",[335,883,884],{"class":772}," }, { ",[335,886,887],{"class":776},"canonicalHost",[335,889,868],{"class":772},[335,891,892],{"class":776},"allowlist",[335,894,895],{"class":772}," }) {\n",[335,897,898,901,904,907,910,913,916,918,921,923,926],{"class":337,"line":456},[335,899,900],{"class":508},"  if",[335,902,903],{"class":772}," (method ",[335,905,906],{"class":508},"!==",[335,908,909],{"class":521}," 'GET'",[335,911,912],{"class":508}," &&",[335,914,915],{"class":772}," method ",[335,917,906],{"class":508},[335,919,920],{"class":521}," 'HEAD'",[335,922,821],{"class":772},[335,924,925],{"class":508},"return",[335,927,928],{"class":512}," false\n",[335,930,931,933,936,939,942,944,947,949,952,954],{"class":337,"line":462},[335,932,900],{"class":508},[335,934,935],{"class":772}," (host.",[335,937,938],{"class":768},"toLowerCase",[335,940,941],{"class":772},"() ",[335,943,906],{"class":508},[335,945,946],{"class":772}," canonicalHost.",[335,948,938],{"class":768},[335,950,951],{"class":772},"()) ",[335,953,925],{"class":508},[335,955,928],{"class":512},[335,957,959,961,964,967,969,972,975,977],{"class":337,"line":958},23,[335,960,900],{"class":508},[335,962,963],{"class":772}," (",[335,965,966],{"class":512},"ASSET_PATH",[335,968,295],{"class":772},[335,970,971],{"class":768},"test",[335,973,974],{"class":772},"(path)) ",[335,976,925],{"class":508},[335,978,928],{"class":512},[335,980,982,984,986,989,992,995,997,999,1001,1003,1006,1009,1011,1014,1017,1019,1021,1024,1026,1028,1031,1034,1037,1040,1043,1045],{"class":337,"line":981},24,[335,983,900],{"class":508},[335,985,963],{"class":772},[335,987,988],{"class":508},"!",[335,990,991],{"class":772},"allowlist.",[335,993,994],{"class":768},"some",[335,996,815],{"class":772},[335,998,223],{"class":776},[335,1000,821],{"class":772},[335,1002,824],{"class":508},[335,1004,1005],{"class":772}," (p ",[335,1007,1008],{"class":508},"===",[335,1010,797],{"class":521},[335,1012,1013],{"class":508}," ?",[335,1015,1016],{"class":772}," path ",[335,1018,1008],{"class":508},[335,1020,797],{"class":521},[335,1022,1023],{"class":508}," :",[335,1025,1016],{"class":772},[335,1027,1008],{"class":508},[335,1029,1030],{"class":772}," p ",[335,1032,1033],{"class":508},"||",[335,1035,1036],{"class":772}," path.",[335,1038,1039],{"class":768},"startsWith",[335,1041,1042],{"class":772},"(p)))) ",[335,1044,925],{"class":508},[335,1046,928],{"class":512},[335,1048,1050,1052,1054,1056,1058],{"class":337,"line":1049},25,[335,1051,785],{"class":508},[335,1053,513],{"class":512},[335,1055,295],{"class":772},[335,1057,971],{"class":768},[335,1059,1060],{"class":772},"(userAgent)\n",[335,1062,1064],{"class":337,"line":1063},26,[335,1065,465],{"class":772},[335,1067,1069],{"class":337,"line":1068},27,[335,1070,372],{"emptyLinePlaceholder":371},[335,1072,1074,1077,1080,1083],{"class":337,"line":1073},28,[335,1075,1076],{"class":508},"let",[335,1078,1079],{"class":772}," warned ",[335,1081,1082],{"class":508},"=",[335,1084,928],{"class":512},[335,1086,1088],{"class":337,"line":1087},29,[335,1089,372],{"emptyLinePlaceholder":371},[335,1091,1093,1095,1098,1100,1103,1105,1108,1110,1113,1115,1118],{"class":337,"line":1092},30,[335,1094,765],{"class":508},[335,1096,1097],{"class":768}," maybeReport",[335,1099,773],{"class":772},[335,1101,1102],{"class":776},"request",[335,1104,868],{"class":772},[335,1106,1107],{"class":776},"response",[335,1109,868],{"class":772},[335,1111,1112],{"class":776},"env",[335,1114,868],{"class":772},[335,1116,1117],{"class":776},"ctx",[335,1119,780],{"class":772},[335,1121,1123,1126,1129,1132,1135,1138],{"class":337,"line":1122},31,[335,1124,1125],{"class":508},"  const",[335,1127,1128],{"class":512}," url",[335,1130,1131],{"class":508}," =",[335,1133,1134],{"class":508}," new",[335,1136,1137],{"class":768}," URL",[335,1139,1140],{"class":772},"(request.url)\n",[335,1142,1144,1146,1149,1151,1154,1157,1159,1162,1164,1166],{"class":337,"line":1143},32,[335,1145,1125],{"class":508},[335,1147,1148],{"class":512}," userAgent",[335,1150,1131],{"class":508},[335,1152,1153],{"class":772}," request.headers.",[335,1155,1156],{"class":768},"get",[335,1158,773],{"class":772},[335,1160,1161],{"class":521},"'user-agent'",[335,1163,821],{"class":772},[335,1165,794],{"class":508},[335,1167,1168],{"class":521}," ''\n",[335,1170,1172],{"class":337,"line":1171},33,[335,1173,372],{"emptyLinePlaceholder":371},[335,1175,1177,1179,1182,1184,1186],{"class":337,"line":1176},34,[335,1178,1125],{"class":508},[335,1180,1181],{"class":512}," report",[335,1183,1131],{"class":508},[335,1185,859],{"class":768},[335,1187,1188],{"class":772},"(\n",[335,1190,1192],{"class":337,"line":1191},35,[335,1193,1194],{"class":772},"    { method: request.method, host: url.hostname, path: url.pathname, userAgent },\n",[335,1196,1198,1201,1204,1207,1210,1213,1216],{"class":337,"line":1197},36,[335,1199,1200],{"class":772},"    { canonicalHost: env.",[335,1202,1203],{"class":512},"CANONICAL_HOST",[335,1205,1206],{"class":772},", allowlist: ",[335,1208,1209],{"class":768},"parseAllowlist",[335,1211,1212],{"class":772},"(env.",[335,1214,1215],{"class":512},"PATH_ALLOWLIST",[335,1217,1218],{"class":772},") },\n",[335,1220,1222],{"class":337,"line":1221},37,[335,1223,1224],{"class":772},"  )\n",[335,1226,1228,1230,1232,1234,1237],{"class":337,"line":1227},38,[335,1229,900],{"class":508},[335,1231,963],{"class":772},[335,1233,988],{"class":508},[335,1235,1236],{"class":772},"report) ",[335,1238,1239],{"class":508},"return\n",[335,1241,1243],{"class":337,"line":1242},39,[335,1244,372],{"emptyLinePlaceholder":371},[335,1246,1248],{"class":337,"line":1247},40,[335,1249,1250],{"class":478},"  \u002F\u002F Set by the edge; a client cannot forge it. Inside a Worker,\n",[335,1252,1254],{"class":337,"line":1253},41,[335,1255,1256],{"class":478},"  \u002F\u002F x-forwarded-for can be forged, so it is not used as a fallback.\n",[335,1258,1260,1262,1265,1267,1269,1271,1273,1276],{"class":337,"line":1259},42,[335,1261,1125],{"class":508},[335,1263,1264],{"class":512}," clientIp",[335,1266,1131],{"class":508},[335,1268,1153],{"class":772},[335,1270,1156],{"class":768},[335,1272,773],{"class":772},[335,1274,1275],{"class":521},"'cf-connecting-ip'",[335,1277,1278],{"class":772},")\n",[335,1280,1282,1284,1286,1288,1291],{"class":337,"line":1281},43,[335,1283,900],{"class":508},[335,1285,963],{"class":772},[335,1287,988],{"class":508},[335,1289,1290],{"class":772},"clientIp) ",[335,1292,1239],{"class":508},[335,1294,1296],{"class":337,"line":1295},44,[335,1297,372],{"emptyLinePlaceholder":371},[335,1299,1301],{"class":337,"line":1300},45,[335,1302,1303],{"class":478},"  \u002F\u002F The query string is never sent: it carries ids on some routes, and a\n",[335,1305,1307],{"class":337,"line":1306},46,[335,1308,1309],{"class":478},"  \u002F\u002F per-page crawl count needs none of it.\n",[335,1311,1313,1315,1318,1320],{"class":337,"line":1312},47,[335,1314,1125],{"class":508},[335,1316,1317],{"class":512}," line",[335,1319,1131],{"class":508},[335,1321,1322],{"class":772}," {\n",[335,1324,1326,1329,1332,1335,1338,1341],{"class":337,"line":1325},48,[335,1327,1328],{"class":772},"    timestamp: ",[335,1330,1331],{"class":508},"new",[335,1333,1334],{"class":768}," Date",[335,1336,1337],{"class":772},"().",[335,1339,1340],{"class":768},"toISOString",[335,1342,1343],{"class":772},"(),\n",[335,1345,1347],{"class":337,"line":1346},49,[335,1348,1349],{"class":772},"    host: url.hostname,\n",[335,1351,1353],{"class":337,"line":1352},50,[335,1354,1355],{"class":772},"    path: url.pathname,\n",[335,1357,1359],{"class":337,"line":1358},51,[335,1360,1361],{"class":772},"    user_agent: userAgent,\n",[335,1363,1365],{"class":337,"line":1364},52,[335,1366,1367],{"class":772},"    status: response.status,\n",[335,1369,1371],{"class":337,"line":1370},53,[335,1372,1373],{"class":772},"    client_ip: clientIp,\n",[335,1375,1377],{"class":337,"line":1376},54,[335,1378,1379],{"class":772},"    method: request.method,\n",[335,1381,1383,1386,1389,1392,1394,1396,1399,1401,1403,1406,1408,1410,1412],{"class":337,"line":1382},55,[335,1384,1385],{"class":772},"    bytes: ",[335,1387,1388],{"class":768},"Number",[335,1390,1391],{"class":772},"(response.headers.",[335,1393,1156],{"class":768},[335,1395,773],{"class":772},[335,1397,1398],{"class":521},"'content-length'",[335,1400,821],{"class":772},[335,1402,794],{"class":508},[335,1404,1405],{"class":512}," 0",[335,1407,821],{"class":772},[335,1409,1033],{"class":508},[335,1411,1405],{"class":512},[335,1413,1414],{"class":772},",\n",[335,1416,1418],{"class":337,"line":1417},56,[335,1419,459],{"class":772},[335,1421,1423],{"class":337,"line":1422},57,[335,1424,372],{"emptyLinePlaceholder":371},[335,1426,1428],{"class":337,"line":1427},58,[335,1429,1430],{"class":478},"  \u002F\u002F One request per hit, deliberately. A Worker isolate is ephemeral and\n",[335,1432,1434],{"class":337,"line":1433},59,[335,1435,1436],{"class":478},"  \u002F\u002F per-colo, so a buffer would be lost on eviction and split across colos.\n",[335,1438,1440],{"class":337,"line":1439},60,[335,1441,1442],{"class":478},"  \u002F\u002F Volume is low because the prefilter already ran.\n",[335,1444,1446,1449,1452],{"class":337,"line":1445},61,[335,1447,1448],{"class":772},"  ctx.",[335,1450,1451],{"class":768},"waitUntil",[335,1453,1188],{"class":772},[335,1455,1457,1460,1462,1465],{"class":337,"line":1456},62,[335,1458,1459],{"class":768},"    fetch",[335,1461,1212],{"class":772},[335,1463,1464],{"class":512},"SHRUWD_INGEST_URL",[335,1466,1467],{"class":772},", {\n",[335,1469,1471,1474,1477],{"class":337,"line":1470},63,[335,1472,1473],{"class":772},"      method: ",[335,1475,1476],{"class":521},"'POST'",[335,1478,1414],{"class":772},[335,1480,1482],{"class":337,"line":1481},64,[335,1483,1484],{"class":772},"      headers: {\n",[335,1486,1488,1491,1494,1497],{"class":337,"line":1487},65,[335,1489,1490],{"class":521},"        'x-shruwd-ingest-token'",[335,1492,1493],{"class":772},": env.",[335,1495,1496],{"class":512},"SHRUWD_INGEST_TOKEN",[335,1498,1414],{"class":772},[335,1500,1502,1505,1508,1511],{"class":337,"line":1501},66,[335,1503,1504],{"class":521},"        'content-type'",[335,1506,1507],{"class":772},": ",[335,1509,1510],{"class":521},"'application\u002Fx-ndjson'",[335,1512,1414],{"class":772},[335,1514,1516],{"class":337,"line":1515},67,[335,1517,1518],{"class":772},"      },\n",[335,1520,1522,1525,1528,1530,1533,1536,1539,1542,1545,1548],{"class":337,"line":1521},68,[335,1523,1524],{"class":772},"      body: ",[335,1526,1527],{"class":512},"JSON",[335,1529,295],{"class":772},[335,1531,1532],{"class":768},"stringify",[335,1534,1535],{"class":772},"(line) ",[335,1537,1538],{"class":508},"+",[335,1540,1541],{"class":521}," '",[335,1543,1544],{"class":512},"\\n",[335,1546,1547],{"class":521},"'",[335,1549,1414],{"class":772},[335,1551,1553],{"class":337,"line":1552},69,[335,1554,1555],{"class":772},"    })\n",[335,1557,1559,1562,1565,1567,1570,1572,1574],{"class":337,"line":1558},70,[335,1560,1561],{"class":772},"      .",[335,1563,1564],{"class":768},"then",[335,1566,815],{"class":772},[335,1568,1569],{"class":776},"res",[335,1571,821],{"class":772},[335,1573,824],{"class":508},[335,1575,1322],{"class":772},[335,1577,1579,1582,1584,1586,1589,1592,1595],{"class":337,"line":1578},71,[335,1580,1581],{"class":508},"        if",[335,1583,963],{"class":772},[335,1585,988],{"class":508},[335,1587,1588],{"class":772},"res.ok ",[335,1590,1591],{"class":508},"&&",[335,1593,1594],{"class":508}," !",[335,1596,1597],{"class":772},"warned) {\n",[335,1599,1601,1604,1606],{"class":337,"line":1600},72,[335,1602,1603],{"class":772},"          warned ",[335,1605,1082],{"class":508},[335,1607,1608],{"class":512}," true\n",[335,1610,1612,1615,1618,1620,1623,1625,1627,1630,1633],{"class":337,"line":1611},73,[335,1613,1614],{"class":772},"          console.",[335,1616,1617],{"class":768},"warn",[335,1619,773],{"class":772},[335,1621,1622],{"class":521},"`shruwd forwarder: ingest answered ${",[335,1624,1569],{"class":772},[335,1626,295],{"class":521},[335,1628,1629],{"class":772},"status",[335,1631,1632],{"class":521},"}; check the token and URL`",[335,1634,1278],{"class":772},[335,1636,1638],{"class":337,"line":1637},74,[335,1639,1640],{"class":772},"        }\n",[335,1642,1644],{"class":337,"line":1643},75,[335,1645,1646],{"class":772},"      })\n",[335,1648,1650,1652,1655,1657,1660,1662,1664],{"class":337,"line":1649},76,[335,1651,1561],{"class":772},[335,1653,1654],{"class":768},"catch",[335,1656,815],{"class":772},[335,1658,1659],{"class":776},"error",[335,1661,821],{"class":772},[335,1663,824],{"class":508},[335,1665,1322],{"class":772},[335,1667,1669,1671,1673,1675],{"class":337,"line":1668},77,[335,1670,1581],{"class":508},[335,1672,963],{"class":772},[335,1674,988],{"class":508},[335,1676,1597],{"class":772},[335,1678,1680,1682,1684],{"class":337,"line":1679},78,[335,1681,1603],{"class":772},[335,1683,1082],{"class":508},[335,1685,1608],{"class":512},[335,1687,1689,1691,1693,1695,1698,1701,1703],{"class":337,"line":1688},79,[335,1690,1614],{"class":772},[335,1692,1617],{"class":768},[335,1694,773],{"class":772},[335,1696,1697],{"class":521},"'shruwd forwarder: ingest unreachable'",[335,1699,1700],{"class":772},", error?.message ",[335,1702,794],{"class":508},[335,1704,1705],{"class":772}," error)\n",[335,1707,1709],{"class":337,"line":1708},80,[335,1710,1640],{"class":772},[335,1712,1714],{"class":337,"line":1713},81,[335,1715,1716],{"class":772},"      }),\n",[335,1718,1720],{"class":337,"line":1719},82,[335,1721,1224],{"class":772},[335,1723,1725],{"class":337,"line":1724},83,[335,1726,465],{"class":772},[335,1728,1730],{"class":337,"line":1729},84,[335,1731,372],{"emptyLinePlaceholder":371},[335,1733,1735,1738,1741],{"class":337,"line":1734},85,[335,1736,1737],{"class":508},"export",[335,1739,1740],{"class":508}," default",[335,1742,1322],{"class":772},[335,1744,1746,1749,1752,1754,1756,1758,1760,1762,1764],{"class":337,"line":1745},86,[335,1747,1748],{"class":508},"  async",[335,1750,1751],{"class":768}," fetch",[335,1753,773],{"class":772},[335,1755,1102],{"class":776},[335,1757,868],{"class":772},[335,1759,1112],{"class":776},[335,1761,868],{"class":772},[335,1763,1117],{"class":776},[335,1765,780],{"class":772},[335,1767,1769],{"class":337,"line":1768},87,[335,1770,1771],{"class":478},"    \u002F\u002F Never inside the try. The only unconditional path is returning your\n",[335,1773,1775],{"class":337,"line":1774},88,[335,1776,1777],{"class":478},"    \u002F\u002F origin's response — a logging pipeline that can break the site it\n",[335,1779,1781],{"class":337,"line":1780},89,[335,1782,1783],{"class":478},"    \u002F\u002F observes is worse than one that loses events.\n",[335,1785,1787,1790,1793,1795,1798,1800],{"class":337,"line":1786},90,[335,1788,1789],{"class":508},"    const",[335,1791,1792],{"class":512}," response",[335,1794,1131],{"class":508},[335,1796,1797],{"class":508}," await",[335,1799,1751],{"class":768},[335,1801,1802],{"class":772},"(request)\n",[335,1804,1806,1809],{"class":337,"line":1805},91,[335,1807,1808],{"class":508},"    try",[335,1810,1322],{"class":772},[335,1812,1814,1817],{"class":337,"line":1813},92,[335,1815,1816],{"class":768},"      maybeReport",[335,1818,1819],{"class":772},"(request, response, env, ctx)\n",[335,1821,1823,1826,1828],{"class":337,"line":1822},93,[335,1824,1825],{"class":772},"    } ",[335,1827,1654],{"class":508},[335,1829,1322],{"class":772},[335,1831,1833],{"class":337,"line":1832},94,[335,1834,1835],{"class":478},"      \u002F\u002F deliberately silent\n",[335,1837,1839],{"class":337,"line":1838},95,[335,1840,1841],{"class":772},"    }\n",[335,1843,1845,1848],{"class":337,"line":1844},96,[335,1846,1847],{"class":508},"    return",[335,1849,1850],{"class":772}," response\n",[335,1852,1854],{"class":337,"line":1853},97,[335,1855,1856],{"class":772},"  },\n",[335,1858,1860],{"class":337,"line":1859},98,[335,1861,465],{"class":772},[254,1863,1865],{"id":1864},"_3-configure-it","3. Configure it",[223,1867,1868,1869,1871],{},"Edit the four values in ",[274,1870,329],{},":",[1873,1874,1875,1888],"table",{},[1876,1877,1878],"thead",{},[1879,1880,1881,1885],"tr",{},[1882,1883,1884],"th",{},"Setting",[1882,1886,1887],{},"Value",[1889,1890,1891,1913,1922,1931],"tbody",{},[1879,1892,1893,1899],{},[1894,1895,1896],"td",{},[274,1897,1898],{},"routes",[1894,1900,1901,1902,1905,1906,1909,1910],{},"Your canonical host, e.g. ",[274,1903,1904],{},"example.com\u002F*"," with ",[274,1907,1908],{},"zone_name"," ",[274,1911,1912],{},"example.com",[1879,1914,1915,1919],{},[1894,1916,1917],{},[274,1918,1203],{},[1894,1920,1921],{},"Your canonical host, without a scheme",[1879,1923,1924,1928],{},[1894,1925,1926],{},[274,1927,1215],{},[1894,1929,1930],{},"Your public, crawlable pages, comma separated",[1879,1932,1933,1937],{},[1894,1934,1935],{},[274,1936,1464],{},[1894,1938,1939,1940,1942],{},"The ",[227,1941,307],{}," from step 1",[235,1944,1945],{"type":237},[223,1946,1947,1949],{},[274,1948,1215],{}," is an allowlist on purpose. Your site probably serves customer content\nand token-bearing links from the same origin, and a blocklist fails open — the next route\nsomeone adds starts forwarding data to a third party. The worst case here is a missing\ndatapoint for a marketing page.",[254,1951,1953],{"id":1952},"_4-deploy","4. Deploy",[223,1955,1956],{},"Store the token as a secret rather than putting it in the config or the source.",[325,1958,1962],{"className":1959,"code":1960,"language":1961,"meta":331,"style":331},"language-bash shiki shiki-themes github-light github-dark","npx wrangler secret put SHRUWD_INGEST_TOKEN\nnpx wrangler deploy\n","bash",[274,1963,1964,1981],{"__ignoreMap":331},[335,1965,1966,1969,1972,1975,1978],{"class":337,"line":338},[335,1967,1968],{"class":768},"npx",[335,1970,1971],{"class":521}," wrangler",[335,1973,1974],{"class":521}," secret",[335,1976,1977],{"class":521}," put",[335,1979,1980],{"class":521}," SHRUWD_INGEST_TOKEN\n",[335,1982,1983,1985,1987],{"class":337,"line":344},[335,1984,1968],{"class":768},[335,1986,1971],{"class":521},[335,1988,1989],{"class":521}," deploy\n",[223,1991,1992],{},"Paste the token when prompted.",[254,1994,1996],{"id":1995},"_5-verify","5. Verify",[223,1998,1999,2000,2003],{},"Request a ",[227,2001,2002],{},"prerendered"," page with a crawler user-agent. That is the case origin\nmiddleware could not see, so it is the one worth testing.",[2005,2006,2007,2040],"code-group",{},[325,2008,2011],{"className":1959,"code":2009,"filename":2010,"language":1961,"meta":331,"style":331},"curl -s -o \u002Fdev\u002Fnull \"https:\u002F\u002Fexample.com\u002Fpricing\" \\\n  -H \"User-Agent: Mozilla\u002F5.0 (compatible; GPTBot\u002F1.2)\"\n","curl",[274,2012,2013,2032],{"__ignoreMap":331},[335,2014,2015,2017,2020,2023,2026,2029],{"class":337,"line":338},[335,2016,2010],{"class":768},[335,2018,2019],{"class":512}," -s",[335,2021,2022],{"class":512}," -o",[335,2024,2025],{"class":521}," \u002Fdev\u002Fnull",[335,2027,2028],{"class":521}," \"https:\u002F\u002Fexample.com\u002Fpricing\"",[335,2030,2031],{"class":512}," \\\n",[335,2033,2034,2037],{"class":337,"line":344},[335,2035,2036],{"class":512},"  -H",[335,2038,2039],{"class":521}," \"User-Agent: Mozilla\u002F5.0 (compatible; GPTBot\u002F1.2)\"\n",[325,2041,2046],{"className":2042,"code":2043,"filename":2044,"language":2045,"meta":331,"style":331},"language-powershell shiki shiki-themes github-light github-dark","Invoke-WebRequest https:\u002F\u002Fexample.com\u002Fpricing `\n  -Headers @{ \"User-Agent\" = \"Mozilla\u002F5.0 (compatible; GPTBot\u002F1.2)\" } | Out-Null\n","PowerShell","powershell",[274,2047,2048,2053],{"__ignoreMap":331},[335,2049,2050],{"class":337,"line":338},[335,2051,2052],{},"Invoke-WebRequest https:\u002F\u002Fexample.com\u002Fpricing `\n",[335,2054,2055],{"class":337,"line":344},[335,2056,2057],{},"  -Headers @{ \"User-Agent\" = \"Mozilla\u002F5.0 (compatible; GPTBot\u002F1.2)\" } | Out-Null\n",[223,2059,2060,2061,2063,2064,2068,2069,295],{},"Within a minute, ",[227,2062,294],{}," moves from ",[2065,2066,2067],"em",{},"awaiting first logs"," to\n",[2065,2070,2071],{},"active",[223,2073,2074,2075,2078,2079,2081],{},"Your test hit comes from your own machine, not from OpenAI, so it is recorded as\n",[227,2076,2077],{},"unverified"," and excluded from headline metrics. That is correct — and the card moving\noff ",[2065,2080,2067],{}," is the signal you are looking for.",[254,2083,2085],{"id":2084},"next-steps","Next steps",[259,2087,2088,2093],{},[262,2089,2090,2092],{},[246,2091,88],{"href":89}," — how verification works",[262,2094,2095,2097],{},[246,2096,94],{"href":95}," — if nothing shows up",[2099,2100,2101],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":331,"searchDepth":350,"depth":350,"links":2103},[2104,2105,2106,2107,2108,2109,2110],{"id":256,"depth":344,"text":257},{"id":284,"depth":344,"text":285},{"id":319,"depth":344,"text":320},{"id":1864,"depth":344,"text":1865},{"id":1952,"depth":344,"text":1953},{"id":1995,"depth":344,"text":1996},{"id":2084,"depth":344,"text":2085},"Deploy a small Cloudflare Worker that forwards AI-crawler hits to Shruwd, including requests your CDN serves from cache.","md",{},{"title":73,"description":76},{"title":73,"description":2111},"Ns6Yt7pHMw5wUqD2OlSiYJ-oS8hu-faCLQbi2GJQ2QY",1790003438810]