[{"data":1,"prerenderedAt":1996},["ShallowReactive",2],{"docs-navigation":3,"docs-\u002Fdocs\u002Fcrawler-logs\u002Fserver-middleware":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":83,"body":215,"description":1990,"extension":1991,"group":71,"meta":1992,"navigation":1993,"path":84,"section":57,"seo":1994,"stem":85,"__hash__":1995},"docs\u002Fdocs\u002F3.crawler-logs\u002F5.server-middleware.md",{"type":216,"value":217,"toc":1981},"minimark",[218,222,226,249,254,267,271,282,286,294,1745,1754,1758,1798,1805,1837,1841,1928,1934,1938,1961,1965,1977],[219,220,83],"h1",{"id":221},"server-middleware",[223,224,225],"p",{},"Middleware in your own app forwards AI-crawler hits to Shruwd. Use it only when no CDN\nanswers for your site.",[227,228,230,237],"callout",{"type":229},"warning",[223,231,232,236],{},[233,234,235],"strong",{},"Read this before you start."," Middleware only sees requests that reach your origin. If\nyour site is static, prerendered or ISR, a CDN answers most requests without ever calling\nyour server — including exactly the marketing pages worth measuring. On one real Nuxt\nsite, eleven of twenty tracked paths never touched the origin at all.",[223,238,239,240,244,245,248],{},"If a CDN sits in front of you, use the ",[241,242,243],"a",{"href":74},"forwarder\nWorker"," or a\n",[241,246,247],{"href":68},"platform drain"," instead. Both see cached\nrequests; this cannot.",[250,251,253],"h2",{"id":252},"prerequisites","Prerequisites",[255,256,257,264],"ul",{},[258,259,260,263],"li",{},[233,261,262],{},"Permissions:"," admin or owner, to create the token.",[258,265,266],{},"A Nuxt or Nitro app whose origin genuinely serves the traffic.",[250,268,270],{"id":269},"_1-get-your-endpoint-and-token","1. Get your endpoint and token",[223,272,273,274,277,278,281],{},"Open the brand's ",[233,275,276],{},"Settings → Crawler logs"," and select ",[233,279,280],{},"Create token",". Copy both\nvalues — the token is shown once, and creating a new one revokes the old one.",[250,283,285],{"id":284},"_2-add-the-middleware","2. Add the middleware",[223,287,288,289,293],{},"Create ",[290,291,292],"code",{},"server\u002Fmiddleware\u002Fcrawler-log.ts"," in your app.",[295,296,301],"pre",{"className":297,"code":298,"filename":292,"language":299,"meta":300,"style":300},"language-ts shiki shiki-themes github-light github-dark","const ENDPOINT = process.env.SHRUWD_INGEST_URL\nconst TOKEN = process.env.SHRUWD_INGEST_TOKEN\n\n\u002F\u002F Flush on whichever comes first. Both are small: a serverless instance can be\n\u002F\u002F frozen between requests, and anything still buffered is lost.\nconst MAX_BATCH = 20\nconst MAX_WAIT_MS = 10_000\n\n\u002F\u002F A deliberately broad prefilter. Real identification happens on Shruwd's side\n\u002F\u002F against a list that is kept current, so this only has to be loose enough not\n\u002F\u002F to drop something that list would have caught.\nconst LOOKS_LIKE_BOT = \u002Fbot|crawl|spider|gpt|claude|perplexity|bytespider|applebot|ccbot|meta-external\u002Fi\n\n\u002F\u002F An ALLOWLIST of your public, crawlable pages — not a list of exclusions.\n\u002F\u002F A blocklist fails open: the next route someone adds starts forwarding\n\u002F\u002F customer data to a third party. Edit this to match your site.\nconst LOGGED_PREFIXES = ['\u002Fdocs', '\u002Ffeatures', '\u002Fpricing', '\u002Fblog', '\u002Ffaq']\n\nfunction isLoggedPath(pathname: string): boolean {\n  if (pathname === '\u002F') return true\n  return LOGGED_PREFIXES.some((p) => pathname === p || pathname.startsWith(p + '\u002F'))\n}\n\nlet buffer: string[] = []\nlet timer: ReturnType\u003Ctypeof setTimeout> | null = null\n\n\u002F\u002F Complain once, then stay quiet. Without this, a missing variable, a revoked\n\u002F\u002F token and \"no crawlers yet\" all look identical.\nlet warned = false\nfunction warnOnce(message: string) {\n  if (warned) return\n  warned = true\n  console.warn(`[shruwd] log drain not working: ${message}`)\n}\n\nfunction flush() {\n  if (timer) { clearTimeout(timer); timer = null }\n  if (buffer.length === 0) return\n\n  if (!ENDPOINT || !TOKEN) {\n    warnOnce('SHRUWD_INGEST_URL or SHRUWD_INGEST_TOKEN is not set')\n    buffer = []\n    return\n  }\n\n  const body = buffer.join('\\n')\n  buffer = []\n\n  \u002F\u002F Not awaited: a network stall must never reach your request path.\n  fetch(ENDPOINT, {\n    method: 'POST',\n    headers: { 'X-Shruwd-Ingest-Token': TOKEN, 'content-type': 'application\u002Fx-ndjson' },\n    body,\n  })\n    .then((res) => {\n      if (!res.ok) warnOnce(`endpoint returned ${res.status}`)\n    })\n    .catch((error) => {\n      warnOnce(`request failed: ${error instanceof Error ? error.message : String(error)}`)\n    })\n}\n\nexport default defineEventHandler((event) => {\n  \u002F\u002F The query string is dropped, not just ignored — it carries ids on some\n  \u002F\u002F routes and a per-page crawl count needs none of it.\n  const pathname = event.path.split('?')[0] ?? '\u002F'\n  if (!isLoggedPath(pathname)) return\n\n  const ua = getRequestHeader(event, 'user-agent') ?? ''\n  if (!LOOKS_LIKE_BOT.test(ua)) return\n\n  \u002F\u002F cf-connecting-ip first: behind Cloudflare it is the only client IP header\n  \u002F\u002F a visitor cannot forge. The first x-forwarded-for entry is the fallback.\n  const ip =\n    getRequestHeader(event, 'cf-connecting-ip') ??\n    (getRequestHeader(event, 'x-forwarded-for') ?? '').split(',')[0]?.trim() ??\n    event.node.req.socket.remoteAddress ??\n    ''\n\n  if (!ip) return\n\n  const started = Date.now()\n\n  \u002F\u002F Status and byte count only exist once the response is finished.\n  event.node.res.once('finish', () => {\n    try {\n      buffer.push(JSON.stringify({\n        timestamp: new Date(started).toISOString(),\n        host: getRequestHeader(event, 'host') ?? '',\n        path: pathname,\n        user_agent: ua,\n        status: event.node.res.statusCode,\n        client_ip: ip,\n        method: event.method,\n        bytes: Number(event.node.res.getHeader('content-length') ?? 0),\n      }))\n\n      if (buffer.length >= MAX_BATCH) flush()\n      else if (!timer) timer = setTimeout(flush, MAX_WAIT_MS)\n    } catch {\n      \u002F\u002F Never throw into the request path.\n    }\n  })\n})\n","ts","",[290,302,303,326,341,348,355,361,374,387,392,398,404,410,480,485,491,497,503,543,548,582,606,658,664,669,691,723,728,734,740,753,773,784,794,816,821,826,837,858,879,884,909,922,932,938,944,949,978,988,993,999,1012,1024,1051,1057,1063,1083,1115,1121,1140,1185,1190,1195,1200,1223,1229,1235,1271,1288,1293,1319,1341,1346,1352,1358,1369,1385,1431,1439,1445,1450,1464,1469,1488,1493,1499,1520,1528,1550,1571,1592,1598,1604,1610,1616,1622,1651,1657,1662,1683,1712,1722,1728,1734,1739],{"__ignoreMap":300},[304,305,308,312,316,319,323],"span",{"class":306,"line":307},"line",1,[304,309,311],{"class":310},"szBVR","const",[304,313,315],{"class":314},"sj4cs"," ENDPOINT",[304,317,318],{"class":310}," =",[304,320,322],{"class":321},"sVt8B"," process.env.",[304,324,325],{"class":314},"SHRUWD_INGEST_URL\n",[304,327,329,331,334,336,338],{"class":306,"line":328},2,[304,330,311],{"class":310},[304,332,333],{"class":314}," TOKEN",[304,335,318],{"class":310},[304,337,322],{"class":321},[304,339,340],{"class":314},"SHRUWD_INGEST_TOKEN\n",[304,342,344],{"class":306,"line":343},3,[304,345,347],{"emptyLinePlaceholder":346},true,"\n",[304,349,351],{"class":306,"line":350},4,[304,352,354],{"class":353},"sJ8bj","\u002F\u002F Flush on whichever comes first. Both are small: a serverless instance can be\n",[304,356,358],{"class":306,"line":357},5,[304,359,360],{"class":353},"\u002F\u002F frozen between requests, and anything still buffered is lost.\n",[304,362,364,366,369,371],{"class":306,"line":363},6,[304,365,311],{"class":310},[304,367,368],{"class":314}," MAX_BATCH",[304,370,318],{"class":310},[304,372,373],{"class":314}," 20\n",[304,375,377,379,382,384],{"class":306,"line":376},7,[304,378,311],{"class":310},[304,380,381],{"class":314}," MAX_WAIT_MS",[304,383,318],{"class":310},[304,385,386],{"class":314}," 10_000\n",[304,388,390],{"class":306,"line":389},8,[304,391,347],{"emptyLinePlaceholder":346},[304,393,395],{"class":306,"line":394},9,[304,396,397],{"class":353},"\u002F\u002F A deliberately broad prefilter. Real identification happens on Shruwd's side\n",[304,399,401],{"class":306,"line":400},10,[304,402,403],{"class":353},"\u002F\u002F against a list that is kept current, so this only has to be loose enough not\n",[304,405,407],{"class":306,"line":406},11,[304,408,409],{"class":353},"\u002F\u002F to drop something that list would have caught.\n",[304,411,413,415,418,420,424,428,431,434,436,439,441,444,446,449,451,454,456,459,461,464,466,469,471,474,477],{"class":306,"line":412},12,[304,414,311],{"class":310},[304,416,417],{"class":314}," LOOKS_LIKE_BOT",[304,419,318],{"class":310},[304,421,423],{"class":422},"sZZnC"," \u002F",[304,425,427],{"class":426},"sA_wV","bot",[304,429,430],{"class":310},"|",[304,432,433],{"class":426},"crawl",[304,435,430],{"class":310},[304,437,438],{"class":426},"spider",[304,440,430],{"class":310},[304,442,443],{"class":426},"gpt",[304,445,430],{"class":310},[304,447,448],{"class":426},"claude",[304,450,430],{"class":310},[304,452,453],{"class":426},"perplexity",[304,455,430],{"class":310},[304,457,458],{"class":426},"bytespider",[304,460,430],{"class":310},[304,462,463],{"class":426},"applebot",[304,465,430],{"class":310},[304,467,468],{"class":426},"ccbot",[304,470,430],{"class":310},[304,472,473],{"class":426},"meta-external",[304,475,476],{"class":422},"\u002F",[304,478,479],{"class":310},"i\n",[304,481,483],{"class":306,"line":482},13,[304,484,347],{"emptyLinePlaceholder":346},[304,486,488],{"class":306,"line":487},14,[304,489,490],{"class":353},"\u002F\u002F An ALLOWLIST of your public, crawlable pages — not a list of exclusions.\n",[304,492,494],{"class":306,"line":493},15,[304,495,496],{"class":353},"\u002F\u002F A blocklist fails open: the next route someone adds starts forwarding\n",[304,498,500],{"class":306,"line":499},16,[304,501,502],{"class":353},"\u002F\u002F customer data to a third party. Edit this to match your site.\n",[304,504,506,508,511,513,516,519,522,525,527,530,532,535,537,540],{"class":306,"line":505},17,[304,507,311],{"class":310},[304,509,510],{"class":314}," LOGGED_PREFIXES",[304,512,318],{"class":310},[304,514,515],{"class":321}," [",[304,517,518],{"class":422},"'\u002Fdocs'",[304,520,521],{"class":321},", ",[304,523,524],{"class":422},"'\u002Ffeatures'",[304,526,521],{"class":321},[304,528,529],{"class":422},"'\u002Fpricing'",[304,531,521],{"class":321},[304,533,534],{"class":422},"'\u002Fblog'",[304,536,521],{"class":321},[304,538,539],{"class":422},"'\u002Ffaq'",[304,541,542],{"class":321},"]\n",[304,544,546],{"class":306,"line":545},18,[304,547,347],{"emptyLinePlaceholder":346},[304,549,551,554,558,561,565,568,571,574,576,579],{"class":306,"line":550},19,[304,552,553],{"class":310},"function",[304,555,557],{"class":556},"sScJk"," isLoggedPath",[304,559,560],{"class":321},"(",[304,562,564],{"class":563},"s4XuR","pathname",[304,566,567],{"class":310},":",[304,569,570],{"class":314}," string",[304,572,573],{"class":321},")",[304,575,567],{"class":310},[304,577,578],{"class":314}," boolean",[304,580,581],{"class":321}," {\n",[304,583,585,588,591,594,597,600,603],{"class":306,"line":584},20,[304,586,587],{"class":310},"  if",[304,589,590],{"class":321}," (pathname ",[304,592,593],{"class":310},"===",[304,595,596],{"class":422}," '\u002F'",[304,598,599],{"class":321},") ",[304,601,602],{"class":310},"return",[304,604,605],{"class":314}," true\n",[304,607,609,612,614,617,620,623,625,627,630,633,635,638,641,644,647,650,653,655],{"class":306,"line":608},21,[304,610,611],{"class":310},"  return",[304,613,510],{"class":314},[304,615,616],{"class":321},".",[304,618,619],{"class":556},"some",[304,621,622],{"class":321},"((",[304,624,223],{"class":563},[304,626,599],{"class":321},[304,628,629],{"class":310},"=>",[304,631,632],{"class":321}," pathname ",[304,634,593],{"class":310},[304,636,637],{"class":321}," p ",[304,639,640],{"class":310},"||",[304,642,643],{"class":321}," pathname.",[304,645,646],{"class":556},"startsWith",[304,648,649],{"class":321},"(p ",[304,651,652],{"class":310},"+",[304,654,596],{"class":422},[304,656,657],{"class":321},"))\n",[304,659,661],{"class":306,"line":660},22,[304,662,663],{"class":321},"}\n",[304,665,667],{"class":306,"line":666},23,[304,668,347],{"emptyLinePlaceholder":346},[304,670,672,675,678,680,682,685,688],{"class":306,"line":671},24,[304,673,674],{"class":310},"let",[304,676,677],{"class":321}," buffer",[304,679,567],{"class":310},[304,681,570],{"class":314},[304,683,684],{"class":321},"[] ",[304,686,687],{"class":310},"=",[304,689,690],{"class":321}," []\n",[304,692,694,696,699,701,704,707,710,713,715,718,720],{"class":306,"line":693},25,[304,695,674],{"class":310},[304,697,698],{"class":321}," timer",[304,700,567],{"class":310},[304,702,703],{"class":556}," ReturnType",[304,705,706],{"class":321},"\u003C",[304,708,709],{"class":310},"typeof",[304,711,712],{"class":321}," setTimeout> ",[304,714,430],{"class":310},[304,716,717],{"class":314}," null",[304,719,318],{"class":310},[304,721,722],{"class":314}," null\n",[304,724,726],{"class":306,"line":725},26,[304,727,347],{"emptyLinePlaceholder":346},[304,729,731],{"class":306,"line":730},27,[304,732,733],{"class":353},"\u002F\u002F Complain once, then stay quiet. Without this, a missing variable, a revoked\n",[304,735,737],{"class":306,"line":736},28,[304,738,739],{"class":353},"\u002F\u002F token and \"no crawlers yet\" all look identical.\n",[304,741,743,745,748,750],{"class":306,"line":742},29,[304,744,674],{"class":310},[304,746,747],{"class":321}," warned ",[304,749,687],{"class":310},[304,751,752],{"class":314}," false\n",[304,754,756,758,761,763,766,768,770],{"class":306,"line":755},30,[304,757,553],{"class":310},[304,759,760],{"class":556}," warnOnce",[304,762,560],{"class":321},[304,764,765],{"class":563},"message",[304,767,567],{"class":310},[304,769,570],{"class":314},[304,771,772],{"class":321},") {\n",[304,774,776,778,781],{"class":306,"line":775},31,[304,777,587],{"class":310},[304,779,780],{"class":321}," (warned) ",[304,782,783],{"class":310},"return\n",[304,785,787,790,792],{"class":306,"line":786},32,[304,788,789],{"class":321},"  warned ",[304,791,687],{"class":310},[304,793,605],{"class":314},[304,795,797,800,803,805,808,810,813],{"class":306,"line":796},33,[304,798,799],{"class":321},"  console.",[304,801,802],{"class":556},"warn",[304,804,560],{"class":321},[304,806,807],{"class":422},"`[shruwd] log drain not working: ${",[304,809,765],{"class":321},[304,811,812],{"class":422},"}`",[304,814,815],{"class":321},")\n",[304,817,819],{"class":306,"line":818},34,[304,820,663],{"class":321},[304,822,824],{"class":306,"line":823},35,[304,825,347],{"emptyLinePlaceholder":346},[304,827,829,831,834],{"class":306,"line":828},36,[304,830,553],{"class":310},[304,832,833],{"class":556}," flush",[304,835,836],{"class":321},"() {\n",[304,838,840,842,845,848,851,853,855],{"class":306,"line":839},37,[304,841,587],{"class":310},[304,843,844],{"class":321}," (timer) { ",[304,846,847],{"class":556},"clearTimeout",[304,849,850],{"class":321},"(timer); timer ",[304,852,687],{"class":310},[304,854,717],{"class":314},[304,856,857],{"class":321}," }\n",[304,859,861,863,866,869,872,875,877],{"class":306,"line":860},38,[304,862,587],{"class":310},[304,864,865],{"class":321}," (buffer.",[304,867,868],{"class":314},"length",[304,870,871],{"class":310}," ===",[304,873,874],{"class":314}," 0",[304,876,599],{"class":321},[304,878,783],{"class":310},[304,880,882],{"class":306,"line":881},39,[304,883,347],{"emptyLinePlaceholder":346},[304,885,887,889,892,895,898,901,904,907],{"class":306,"line":886},40,[304,888,587],{"class":310},[304,890,891],{"class":321}," (",[304,893,894],{"class":310},"!",[304,896,897],{"class":314},"ENDPOINT",[304,899,900],{"class":310}," ||",[304,902,903],{"class":310}," !",[304,905,906],{"class":314},"TOKEN",[304,908,772],{"class":321},[304,910,912,915,917,920],{"class":306,"line":911},41,[304,913,914],{"class":556},"    warnOnce",[304,916,560],{"class":321},[304,918,919],{"class":422},"'SHRUWD_INGEST_URL or SHRUWD_INGEST_TOKEN is not set'",[304,921,815],{"class":321},[304,923,925,928,930],{"class":306,"line":924},42,[304,926,927],{"class":321},"    buffer ",[304,929,687],{"class":310},[304,931,690],{"class":321},[304,933,935],{"class":306,"line":934},43,[304,936,937],{"class":310},"    return\n",[304,939,941],{"class":306,"line":940},44,[304,942,943],{"class":321},"  }\n",[304,945,947],{"class":306,"line":946},45,[304,948,347],{"emptyLinePlaceholder":346},[304,950,952,955,958,960,963,966,968,971,974,976],{"class":306,"line":951},46,[304,953,954],{"class":310},"  const",[304,956,957],{"class":314}," body",[304,959,318],{"class":310},[304,961,962],{"class":321}," buffer.",[304,964,965],{"class":556},"join",[304,967,560],{"class":321},[304,969,970],{"class":422},"'",[304,972,973],{"class":314},"\\n",[304,975,970],{"class":422},[304,977,815],{"class":321},[304,979,981,984,986],{"class":306,"line":980},47,[304,982,983],{"class":321},"  buffer ",[304,985,687],{"class":310},[304,987,690],{"class":321},[304,989,991],{"class":306,"line":990},48,[304,992,347],{"emptyLinePlaceholder":346},[304,994,996],{"class":306,"line":995},49,[304,997,998],{"class":353},"  \u002F\u002F Not awaited: a network stall must never reach your request path.\n",[304,1000,1002,1005,1007,1009],{"class":306,"line":1001},50,[304,1003,1004],{"class":556},"  fetch",[304,1006,560],{"class":321},[304,1008,897],{"class":314},[304,1010,1011],{"class":321},", {\n",[304,1013,1015,1018,1021],{"class":306,"line":1014},51,[304,1016,1017],{"class":321},"    method: ",[304,1019,1020],{"class":422},"'POST'",[304,1022,1023],{"class":321},",\n",[304,1025,1027,1030,1033,1036,1038,1040,1043,1045,1048],{"class":306,"line":1026},52,[304,1028,1029],{"class":321},"    headers: { ",[304,1031,1032],{"class":422},"'X-Shruwd-Ingest-Token'",[304,1034,1035],{"class":321},": ",[304,1037,906],{"class":314},[304,1039,521],{"class":321},[304,1041,1042],{"class":422},"'content-type'",[304,1044,1035],{"class":321},[304,1046,1047],{"class":422},"'application\u002Fx-ndjson'",[304,1049,1050],{"class":321}," },\n",[304,1052,1054],{"class":306,"line":1053},53,[304,1055,1056],{"class":321},"    body,\n",[304,1058,1060],{"class":306,"line":1059},54,[304,1061,1062],{"class":321},"  })\n",[304,1064,1066,1069,1072,1074,1077,1079,1081],{"class":306,"line":1065},55,[304,1067,1068],{"class":321},"    .",[304,1070,1071],{"class":556},"then",[304,1073,622],{"class":321},[304,1075,1076],{"class":563},"res",[304,1078,599],{"class":321},[304,1080,629],{"class":310},[304,1082,581],{"class":321},[304,1084,1086,1089,1091,1093,1096,1099,1101,1104,1106,1108,1111,1113],{"class":306,"line":1085},56,[304,1087,1088],{"class":310},"      if",[304,1090,891],{"class":321},[304,1092,894],{"class":310},[304,1094,1095],{"class":321},"res.ok) ",[304,1097,1098],{"class":556},"warnOnce",[304,1100,560],{"class":321},[304,1102,1103],{"class":422},"`endpoint returned ${",[304,1105,1076],{"class":321},[304,1107,616],{"class":422},[304,1109,1110],{"class":321},"status",[304,1112,812],{"class":422},[304,1114,815],{"class":321},[304,1116,1118],{"class":306,"line":1117},57,[304,1119,1120],{"class":321},"    })\n",[304,1122,1124,1126,1129,1131,1134,1136,1138],{"class":306,"line":1123},58,[304,1125,1068],{"class":321},[304,1127,1128],{"class":556},"catch",[304,1130,622],{"class":321},[304,1132,1133],{"class":563},"error",[304,1135,599],{"class":321},[304,1137,629],{"class":310},[304,1139,581],{"class":321},[304,1141,1143,1146,1148,1151,1153,1156,1159,1162,1165,1167,1169,1172,1175,1177,1179,1181,1183],{"class":306,"line":1142},59,[304,1144,1145],{"class":556},"      warnOnce",[304,1147,560],{"class":321},[304,1149,1150],{"class":422},"`request failed: ${",[304,1152,1133],{"class":321},[304,1154,1155],{"class":310}," instanceof",[304,1157,1158],{"class":556}," Error",[304,1160,1161],{"class":310}," ?",[304,1163,1164],{"class":321}," error",[304,1166,616],{"class":422},[304,1168,765],{"class":321},[304,1170,1171],{"class":310}," :",[304,1173,1174],{"class":556}," String",[304,1176,560],{"class":422},[304,1178,1133],{"class":321},[304,1180,573],{"class":422},[304,1182,812],{"class":422},[304,1184,815],{"class":321},[304,1186,1188],{"class":306,"line":1187},60,[304,1189,1120],{"class":321},[304,1191,1193],{"class":306,"line":1192},61,[304,1194,663],{"class":321},[304,1196,1198],{"class":306,"line":1197},62,[304,1199,347],{"emptyLinePlaceholder":346},[304,1201,1203,1206,1209,1212,1214,1217,1219,1221],{"class":306,"line":1202},63,[304,1204,1205],{"class":310},"export",[304,1207,1208],{"class":310}," default",[304,1210,1211],{"class":556}," defineEventHandler",[304,1213,622],{"class":321},[304,1215,1216],{"class":563},"event",[304,1218,599],{"class":321},[304,1220,629],{"class":310},[304,1222,581],{"class":321},[304,1224,1226],{"class":306,"line":1225},64,[304,1227,1228],{"class":353},"  \u002F\u002F The query string is dropped, not just ignored — it carries ids on some\n",[304,1230,1232],{"class":306,"line":1231},65,[304,1233,1234],{"class":353},"  \u002F\u002F routes and a per-page crawl count needs none of it.\n",[304,1236,1238,1240,1243,1245,1248,1251,1253,1256,1259,1262,1265,1268],{"class":306,"line":1237},66,[304,1239,954],{"class":310},[304,1241,1242],{"class":314}," pathname",[304,1244,318],{"class":310},[304,1246,1247],{"class":321}," event.path.",[304,1249,1250],{"class":556},"split",[304,1252,560],{"class":321},[304,1254,1255],{"class":422},"'?'",[304,1257,1258],{"class":321},")[",[304,1260,1261],{"class":314},"0",[304,1263,1264],{"class":321},"] ",[304,1266,1267],{"class":310},"??",[304,1269,1270],{"class":422}," '\u002F'\n",[304,1272,1274,1276,1278,1280,1283,1286],{"class":306,"line":1273},67,[304,1275,587],{"class":310},[304,1277,891],{"class":321},[304,1279,894],{"class":310},[304,1281,1282],{"class":556},"isLoggedPath",[304,1284,1285],{"class":321},"(pathname)) ",[304,1287,783],{"class":310},[304,1289,1291],{"class":306,"line":1290},68,[304,1292,347],{"emptyLinePlaceholder":346},[304,1294,1296,1298,1301,1303,1306,1309,1312,1314,1316],{"class":306,"line":1295},69,[304,1297,954],{"class":310},[304,1299,1300],{"class":314}," ua",[304,1302,318],{"class":310},[304,1304,1305],{"class":556}," getRequestHeader",[304,1307,1308],{"class":321},"(event, ",[304,1310,1311],{"class":422},"'user-agent'",[304,1313,599],{"class":321},[304,1315,1267],{"class":310},[304,1317,1318],{"class":422}," ''\n",[304,1320,1322,1324,1326,1328,1331,1333,1336,1339],{"class":306,"line":1321},70,[304,1323,587],{"class":310},[304,1325,891],{"class":321},[304,1327,894],{"class":310},[304,1329,1330],{"class":314},"LOOKS_LIKE_BOT",[304,1332,616],{"class":321},[304,1334,1335],{"class":556},"test",[304,1337,1338],{"class":321},"(ua)) ",[304,1340,783],{"class":310},[304,1342,1344],{"class":306,"line":1343},71,[304,1345,347],{"emptyLinePlaceholder":346},[304,1347,1349],{"class":306,"line":1348},72,[304,1350,1351],{"class":353},"  \u002F\u002F cf-connecting-ip first: behind Cloudflare it is the only client IP header\n",[304,1353,1355],{"class":306,"line":1354},73,[304,1356,1357],{"class":353},"  \u002F\u002F a visitor cannot forge. The first x-forwarded-for entry is the fallback.\n",[304,1359,1361,1363,1366],{"class":306,"line":1360},74,[304,1362,954],{"class":310},[304,1364,1365],{"class":314}," ip",[304,1367,1368],{"class":310}," =\n",[304,1370,1372,1375,1377,1380,1382],{"class":306,"line":1371},75,[304,1373,1374],{"class":556},"    getRequestHeader",[304,1376,1308],{"class":321},[304,1378,1379],{"class":422},"'cf-connecting-ip'",[304,1381,599],{"class":321},[304,1383,1384],{"class":310},"??\n",[304,1386,1388,1391,1394,1396,1399,1401,1403,1406,1409,1411,1413,1416,1418,1420,1423,1426,1429],{"class":306,"line":1387},76,[304,1389,1390],{"class":321},"    (",[304,1392,1393],{"class":556},"getRequestHeader",[304,1395,1308],{"class":321},[304,1397,1398],{"class":422},"'x-forwarded-for'",[304,1400,599],{"class":321},[304,1402,1267],{"class":310},[304,1404,1405],{"class":422}," ''",[304,1407,1408],{"class":321},").",[304,1410,1250],{"class":556},[304,1412,560],{"class":321},[304,1414,1415],{"class":422},"','",[304,1417,1258],{"class":321},[304,1419,1261],{"class":314},[304,1421,1422],{"class":321},"]?.",[304,1424,1425],{"class":556},"trim",[304,1427,1428],{"class":321},"() ",[304,1430,1384],{"class":310},[304,1432,1434,1437],{"class":306,"line":1433},77,[304,1435,1436],{"class":321},"    event.node.req.socket.remoteAddress ",[304,1438,1384],{"class":310},[304,1440,1442],{"class":306,"line":1441},78,[304,1443,1444],{"class":422},"    ''\n",[304,1446,1448],{"class":306,"line":1447},79,[304,1449,347],{"emptyLinePlaceholder":346},[304,1451,1453,1455,1457,1459,1462],{"class":306,"line":1452},80,[304,1454,587],{"class":310},[304,1456,891],{"class":321},[304,1458,894],{"class":310},[304,1460,1461],{"class":321},"ip) ",[304,1463,783],{"class":310},[304,1465,1467],{"class":306,"line":1466},81,[304,1468,347],{"emptyLinePlaceholder":346},[304,1470,1472,1474,1477,1479,1482,1485],{"class":306,"line":1471},82,[304,1473,954],{"class":310},[304,1475,1476],{"class":314}," started",[304,1478,318],{"class":310},[304,1480,1481],{"class":321}," Date.",[304,1483,1484],{"class":556},"now",[304,1486,1487],{"class":321},"()\n",[304,1489,1491],{"class":306,"line":1490},83,[304,1492,347],{"emptyLinePlaceholder":346},[304,1494,1496],{"class":306,"line":1495},84,[304,1497,1498],{"class":353},"  \u002F\u002F Status and byte count only exist once the response is finished.\n",[304,1500,1502,1505,1508,1510,1513,1516,1518],{"class":306,"line":1501},85,[304,1503,1504],{"class":321},"  event.node.res.",[304,1506,1507],{"class":556},"once",[304,1509,560],{"class":321},[304,1511,1512],{"class":422},"'finish'",[304,1514,1515],{"class":321},", () ",[304,1517,629],{"class":310},[304,1519,581],{"class":321},[304,1521,1523,1526],{"class":306,"line":1522},86,[304,1524,1525],{"class":310},"    try",[304,1527,581],{"class":321},[304,1529,1531,1534,1537,1539,1542,1544,1547],{"class":306,"line":1530},87,[304,1532,1533],{"class":321},"      buffer.",[304,1535,1536],{"class":556},"push",[304,1538,560],{"class":321},[304,1540,1541],{"class":314},"JSON",[304,1543,616],{"class":321},[304,1545,1546],{"class":556},"stringify",[304,1548,1549],{"class":321},"({\n",[304,1551,1553,1556,1559,1562,1565,1568],{"class":306,"line":1552},88,[304,1554,1555],{"class":321},"        timestamp: ",[304,1557,1558],{"class":310},"new",[304,1560,1561],{"class":556}," Date",[304,1563,1564],{"class":321},"(started).",[304,1566,1567],{"class":556},"toISOString",[304,1569,1570],{"class":321},"(),\n",[304,1572,1574,1577,1579,1581,1584,1586,1588,1590],{"class":306,"line":1573},89,[304,1575,1576],{"class":321},"        host: ",[304,1578,1393],{"class":556},[304,1580,1308],{"class":321},[304,1582,1583],{"class":422},"'host'",[304,1585,599],{"class":321},[304,1587,1267],{"class":310},[304,1589,1405],{"class":422},[304,1591,1023],{"class":321},[304,1593,1595],{"class":306,"line":1594},90,[304,1596,1597],{"class":321},"        path: pathname,\n",[304,1599,1601],{"class":306,"line":1600},91,[304,1602,1603],{"class":321},"        user_agent: ua,\n",[304,1605,1607],{"class":306,"line":1606},92,[304,1608,1609],{"class":321},"        status: event.node.res.statusCode,\n",[304,1611,1613],{"class":306,"line":1612},93,[304,1614,1615],{"class":321},"        client_ip: ip,\n",[304,1617,1619],{"class":306,"line":1618},94,[304,1620,1621],{"class":321},"        method: event.method,\n",[304,1623,1625,1628,1631,1634,1637,1639,1642,1644,1646,1648],{"class":306,"line":1624},95,[304,1626,1627],{"class":321},"        bytes: ",[304,1629,1630],{"class":556},"Number",[304,1632,1633],{"class":321},"(event.node.res.",[304,1635,1636],{"class":556},"getHeader",[304,1638,560],{"class":321},[304,1640,1641],{"class":422},"'content-length'",[304,1643,599],{"class":321},[304,1645,1267],{"class":310},[304,1647,874],{"class":314},[304,1649,1650],{"class":321},"),\n",[304,1652,1654],{"class":306,"line":1653},96,[304,1655,1656],{"class":321},"      }))\n",[304,1658,1660],{"class":306,"line":1659},97,[304,1661,347],{"emptyLinePlaceholder":346},[304,1663,1665,1667,1669,1671,1674,1676,1678,1681],{"class":306,"line":1664},98,[304,1666,1088],{"class":310},[304,1668,865],{"class":321},[304,1670,868],{"class":314},[304,1672,1673],{"class":310}," >=",[304,1675,368],{"class":314},[304,1677,599],{"class":321},[304,1679,1680],{"class":556},"flush",[304,1682,1487],{"class":321},[304,1684,1686,1689,1692,1694,1696,1699,1701,1704,1707,1710],{"class":306,"line":1685},99,[304,1687,1688],{"class":310},"      else",[304,1690,1691],{"class":310}," if",[304,1693,891],{"class":321},[304,1695,894],{"class":310},[304,1697,1698],{"class":321},"timer) timer ",[304,1700,687],{"class":310},[304,1702,1703],{"class":556}," setTimeout",[304,1705,1706],{"class":321},"(flush, ",[304,1708,1709],{"class":314},"MAX_WAIT_MS",[304,1711,815],{"class":321},[304,1713,1715,1718,1720],{"class":306,"line":1714},100,[304,1716,1717],{"class":321},"    } ",[304,1719,1128],{"class":310},[304,1721,581],{"class":321},[304,1723,1725],{"class":306,"line":1724},101,[304,1726,1727],{"class":353},"      \u002F\u002F Never throw into the request path.\n",[304,1729,1731],{"class":306,"line":1730},102,[304,1732,1733],{"class":321},"    }\n",[304,1735,1737],{"class":306,"line":1736},103,[304,1738,1062],{"class":321},[304,1740,1742],{"class":306,"line":1741},104,[304,1743,1744],{"class":321},"})\n",[223,1746,1747,1753],{},[233,1748,1749,1750],{},"Edit ",[290,1751,1752],{},"LOGGED_PREFIXES"," to your own public pages before deploying. It is an allowlist\non purpose: your app probably serves customer content and token-bearing links from the\nsame origin, and none of that should leave your servers.",[250,1755,1757],{"id":1756},"_3-set-the-variables","3. Set the variables",[1759,1760,1761,1774],"table",{},[1762,1763,1764],"thead",{},[1765,1766,1767,1771],"tr",{},[1768,1769,1770],"th",{},"Variable",[1768,1772,1773],{},"Value",[1775,1776,1777,1788],"tbody",{},[1765,1778,1779,1785],{},[1780,1781,1782],"td",{},[290,1783,1784],{},"SHRUWD_INGEST_URL",[1780,1786,1787],{},"the endpoint from step 1",[1765,1789,1790,1795],{},[1780,1791,1792],{},[290,1793,1794],{},"SHRUWD_INGEST_TOKEN",[1780,1796,1797],{},"the token from step 1",[223,1799,1800,1801,1804],{},"Set them in your ",[233,1802,1803],{},"deployed"," environment, not only locally. That omission is the most\ncommon reason nothing arrives.",[227,1806,1807],{"type":229},[223,1808,1809,1810,1813,1814,1817,1818,521,1821,1824,1825,1828,1829,1832,1833,1836],{},"If you switch to ",[290,1811,1812],{},"useRuntimeConfig()"," instead of ",[290,1815,1816],{},"process.env",", two things must change\ntogether. Declare the keys in ",[290,1819,1820],{},"nuxt.config.ts",[233,1822,1823],{},"and"," prefix the variables with ",[290,1826,1827],{},"NUXT_","\n(",[290,1830,1831],{},"NUXT_SHRUWD_INGEST_URL","). Miss either and the config reads ",[290,1834,1835],{},"undefined",", the middleware\nreturns at its first check, and nothing is ever sent — with no error anywhere.",[250,1838,1840],{"id":1839},"_4-verify","4. Verify",[1842,1843,1844,1895],"code-group",{},[295,1845,1850],{"className":1846,"code":1847,"filename":1848,"language":1849,"meta":300,"style":300},"language-bash shiki shiki-themes github-light github-dark","curl -X POST \"YOUR_ENDPOINT?validate=1\" \\\n  -H \"X-Shruwd-Ingest-Token: YOUR_TOKEN\" \\\n  -H \"Content-Type: application\u002Fx-ndjson\" \\\n  -d '{\"timestamp\":\"2026-01-01T00:00:00Z\",\"host\":\"example.com\",\"path\":\"\u002F\",\"user_agent\":\"GPTBot\",\"status\":200,\"client_ip\":\"1.2.3.4\"}'\n","curl","bash",[290,1851,1852,1868,1878,1887],{"__ignoreMap":300},[304,1853,1854,1856,1859,1862,1865],{"class":306,"line":307},[304,1855,1848],{"class":556},[304,1857,1858],{"class":314}," -X",[304,1860,1861],{"class":422}," POST",[304,1863,1864],{"class":422}," \"YOUR_ENDPOINT?validate=1\"",[304,1866,1867],{"class":314}," \\\n",[304,1869,1870,1873,1876],{"class":306,"line":328},[304,1871,1872],{"class":314},"  -H",[304,1874,1875],{"class":422}," \"X-Shruwd-Ingest-Token: YOUR_TOKEN\"",[304,1877,1867],{"class":314},[304,1879,1880,1882,1885],{"class":306,"line":343},[304,1881,1872],{"class":314},[304,1883,1884],{"class":422}," \"Content-Type: application\u002Fx-ndjson\"",[304,1886,1867],{"class":314},[304,1888,1889,1892],{"class":306,"line":350},[304,1890,1891],{"class":314},"  -d",[304,1893,1894],{"class":422}," '{\"timestamp\":\"2026-01-01T00:00:00Z\",\"host\":\"example.com\",\"path\":\"\u002F\",\"user_agent\":\"GPTBot\",\"status\":200,\"client_ip\":\"1.2.3.4\"}'\n",[295,1896,1901],{"className":1897,"code":1898,"filename":1899,"language":1900,"meta":300,"style":300},"language-powershell shiki shiki-themes github-light github-dark","Invoke-RestMethod -Method Post `\n  -Uri \"YOUR_ENDPOINT?validate=1\" `\n  -Headers @{ \"X-Shruwd-Ingest-Token\" = \"YOUR_TOKEN\" } `\n  -Body '{\"timestamp\":\"2026-01-01T00:00:00Z\",\"host\":\"example.com\",\"path\":\"\u002F\",\"user_agent\":\"GPTBot\",\"status\":200,\"client_ip\":\"1.2.3.4\"}' `\n  -ContentType \"application\u002Fx-ndjson\"\n","PowerShell","powershell",[290,1902,1903,1908,1913,1918,1923],{"__ignoreMap":300},[304,1904,1905],{"class":306,"line":307},[304,1906,1907],{},"Invoke-RestMethod -Method Post `\n",[304,1909,1910],{"class":306,"line":328},[304,1911,1912],{},"  -Uri \"YOUR_ENDPOINT?validate=1\" `\n",[304,1914,1915],{"class":306,"line":343},[304,1916,1917],{},"  -Headers @{ \"X-Shruwd-Ingest-Token\" = \"YOUR_TOKEN\" } `\n",[304,1919,1920],{"class":306,"line":350},[304,1921,1922],{},"  -Body '{\"timestamp\":\"2026-01-01T00:00:00Z\",\"host\":\"example.com\",\"path\":\"\u002F\",\"user_agent\":\"GPTBot\",\"status\":200,\"client_ip\":\"1.2.3.4\"}' `\n",[304,1924,1925],{"class":306,"line":357},[304,1926,1927],{},"  -ContentType \"application\u002Fx-ndjson\"\n",[223,1929,1930,1933],{},[290,1931,1932],{},"usable: true"," means the endpoint and token are good, so any remaining problem is in your\napp. Then deploy and wait for real crawler traffic — the buffer flushes at 20 events or\n10 seconds, so a single test request will not appear instantly.",[250,1935,1937],{"id":1936},"known-limits","Known limits",[255,1939,1940,1949,1955],{},[258,1941,1942,1945,1946,1948],{},[233,1943,1944],{},"Cached responses are invisible."," A request your CDN serves never reaches the\nmiddleware. \"Zero hits on ",[290,1947,476],{},"\" can mean \"cached\", not \"never crawled\".",[258,1950,1951,1954],{},[233,1952,1953],{},"Buffered events are lost"," when a serverless instance is reclaimed. Crawler hits feed\nrates and per-page counts, so losing a small fraction evenly does not bias them.",[258,1956,1957,1960],{},[233,1958,1959],{},"A brand-new crawler can slip the prefilter"," until the pattern is widened.",[250,1962,1964],{"id":1963},"next-steps","Next steps",[255,1966,1967,1972],{},[258,1968,1969,1971],{},[241,1970,94],{"href":95}," — step-by-step diagnosis",[258,1973,1974,1976],{},[241,1975,88],{"href":89}," — what gets recognised",[1978,1979,1980],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":300,"searchDepth":343,"depth":343,"links":1982},[1983,1984,1985,1986,1987,1988,1989],{"id":252,"depth":328,"text":253},{"id":269,"depth":328,"text":270},{"id":284,"depth":328,"text":285},{"id":1756,"depth":328,"text":1757},{"id":1839,"depth":328,"text":1840},{"id":1936,"depth":328,"text":1937},{"id":1963,"depth":328,"text":1964},"Forward AI-crawler hits from a Nuxt or Nitro origin — only when no CDN answers for your site.","md",{},{"title":83,"description":86},{"title":83,"description":1990},"g8cOrmHbk2jrO0uT3QqZ42OaE-xyxKTW8bgkdktKymQ",1790003438900]