> ## Documentation Index
> Fetch the complete documentation index at: https://snowseo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Express

> Track AI crawlers on Express and other Node HTTP servers.

<Steps>
  <Step title="Install">
    ```bash theme={null}
    npm install @snowseo/beacon
    ```
  </Step>

  <Step title="Set your site key">
    ```bash .env theme={null}
    SNOWSEO_BEACON_KEY=sb_live_...
    ```
  </Step>

  <Step title="Mount the middleware">
    ```ts theme={null}
    import { createBeacon } from "@snowseo/beacon";
    import { beaconExpress } from "@snowseo/beacon/node";

    const beacon = createBeacon({
      siteUrl: "https://example.com",
      analytics: { key: process.env.SNOWSEO_BEACON_KEY! },
    });

    app.use(beaconExpress(beacon));
    ```

    Mount it before your routes so every request passes through it.
  </Step>
</Steps>

Verify:

```bash theme={null}
curl -A "GPTBot/1.2" https://example.com/
```

***

## Behind a proxy

If Express sits behind nginx, a load balancer or Cloudflare, make sure the real client address reaches it - the SDK reads `CF-Connecting-IP`, `X-Real-IP`, `True-Client-IP` and `X-Forwarded-For`, in that order.

Without a real address, crawlers cannot be verified against published IP ranges and every hit shows as **Unverified**.

***

## Response status

Markdown hits carry the real `statusCode`; HTML hits usually do not. That is deliberate rather than an oversight - the middleware sees the response before your route has run, so its `200` is a default, and reporting it would log every 404 as a success.

Supply `ctx.statusCode` only where you have the genuine post-route status.
