Skip to content

TanStack Start Bot & Crawler Detection

Crawler detection runs server-side, so this applies to TanStack Start (SSR). Register a global request middleware — it runs on every server request, including the SSR document loads that non-JS crawlers fetch — and forward the visitor's signals to Kitbase. Bot/crawler traffic is stored with attribution; human requests are ignored.

Privacy — we only keep the bots

Forwarding every request doesn't mean every request is stored. Human visitors' signals are used only to classify the request in memory and are then discarded — only bot and crawler requests are persisted. For those, the raw IP is stored only when IP logging is enabled for the environment; otherwise it's used to derive geolocation (country, region, city) and then dropped.

Prerequisites

Set two environment variables on your server:

  • KITBASE_API_KEY — your project's secret API key (sk_kitbase_…), not the browser SDK key.
  • KITBASE_ENVIRONMENT — the target environment name, e.g. Production.

Setup

Create a src/start.ts file (it isn't in the default template) and register the middleware globally:

ts
// src/start.ts
import { createStart, createMiddleware } from "@tanstack/react-start";

const crawlerIngest = createMiddleware().server(({ next, request }) => {
  const url = new URL(request.url);
  const fwd = request.headers.get("x-forwarded-for");
  fetch("https://ingest.kitbase.dev/ingest/v1/server", {
    method: "POST",
    headers: { authorization: `Bearer ${process.env.KITBASE_API_KEY}`, "content-type": "application/json" },
    body: JSON.stringify({ environment: process.env.KITBASE_ENVIRONMENT, events: [{
      user_agent: request.headers.get("user-agent"),
      ip_address: fwd?.split(",")[0]?.trim(),
      method: request.method,
      host: url.host,
      path: url.pathname,
      referrer: request.headers.get("referer"),
      signature: request.headers.get("signature"),
      signature_input: request.headers.get("signature-input"),
      signature_agent: request.headers.get("signature-agent"),
    }]}),
  }).catch(() => {}); // fire-and-forget; never block SSR

  return next();
});

export const startInstance = createStart(() => ({
  requestMiddleware: [crawlerIngest],
}));

The signature* headers are part of Web Bot Auth — forwarding them lets Kitbase cryptographically verify a crawler's claimed identity when present.

Using TanStack Router as a client-only SPA (no SSR)?

A non-JS crawler only ever receives your static index.html and never runs the bundle, so the router can't observe it. Forward from whatever serves that HTML instead — your Node/Express or Cloudflare host, or, on Vercel, a Log Drain.

Next steps

  • API reference — full request schema, response, and attribution fields.
  • All platforms — setup guides for other frameworks and hosts.

Released under the MIT License.