---
description: Install UsherStats with the snippet, a first-party path on your own domain, the Worker SDK, or proxy mode.
---
# Installing

There are three ways to install UsherStats, plus proxy mode, and they build on each other.

| Way | What you get | What you change |
|---|---|---|
| [Snippet](#the-snippet) | Human analytics, goals, journeys, A/B tests | One tag in your pages |
| [First-party path](#a-first-party-path) | The same, served from your own domain, so blockers aimed at third parties leave it alone | A route on your server |
| [Worker SDK](#the-worker-sdk) | All of the above, plus crawler and AI-bot analytics and bot protection | Your Cloudflare Worker |
| [Proxy mode](#proxy-mode) | The same as the Worker SDK, with no code | A DNS record |

Your site's **public key** (`pk_…`) and **server secret** (`sk_live_…`) are on its Install page in the dashboard. The
public key is public by design. Keep the server secret on your server: it is what lets UsherStats believe what your
server says about its visitors.

## The snippet

```html
<script defer src="https://collect.usherstats.com/t.js" data-site="pk_…"></script>
```

Put it in the `<head>` of every page. Optional attributes:

- `data-page-type="article"`: a short label for the kind of page, for the dashboard's page-type views.
- `data-journeys="off"`: send pageviews and events only, not journeys.

If your site sends a Content Security Policy, allow `https://collect.usherstats.com` in `script-src` and `connect-src`.

## A first-party path

Served from your own domain, the script and its reports are first-party: nothing for a blocker to drop as a third party.
Your server forwards `/_us/` to `https://collect.usherstats.com/`, and the snippet says so:

```html
<script defer src="/_us/t.js" data-site="pk_…" data-api="/_us"></script>
```

**Your server must tell UsherStats who the visitor is.** Behind your proxy, UsherStats sees your server, not the
visitor, and a server's network is a hosting network, which UsherStats counts as automated. So every forwarded request
carries your server secret and the visitor's details:

| Header | Value |
|---|---|
| `x-usher-proxy` | your server secret, `sk_live_…` |
| `x-usher-client-ip` | the visitor's IP address |
| `x-usher-client-country` | the visitor's two-letter country code |
| `x-usher-client-asn` | the visitor's network number (AS number) |
| `x-usher-client-as-org` | the visitor's network name, such as `Comcast Cable Communications, LLC` |

UsherStats uses these only with a valid secret for that site. **If your server cannot supply the visitor's network
(AS number and name), use the snippet directly instead**: otherwise your readers will be counted as hosting-network
traffic. On Cloudflare, the Worker SDK fills all of them for you. Elsewhere, a GeoIP database with network data (for
example MaxMind's GeoLite2 ASN and Country) provides them.

### Cloudflare Workers

Use the [Worker SDK](#the-worker-sdk): it serves `/_us/` and adds the snippet to your pages for you.

### Next.js

Most Next.js hosts do not tell your code the visitor's network, so for Next.js the simplest correct install is the
direct snippet, in your root layout:

```jsx
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head>
        <Script src="https://collect.usherstats.com/t.js" data-site="pk_…" strategy="afterInteractive" />
      </head>
      <body>{children}</body>
    </html>
  );
}
```

If your Next.js site runs behind Cloudflare, put a Worker with the [Worker SDK](#the-worker-sdk) in front of it for a
first-party path. If your host gives you the visitor's network in request headers, you can forward `/_us/` with a
route handler that sets the headers above, as in the Nginx example.

### Nginx

With the [GeoIP2 module](https://github.com/leev/ngx_http_geoip2_module) and the GeoLite2 ASN and Country databases
loaded as `$geoip2_asn`, `$geoip2_as_org` and `$geoip2_country`:

```nginx
location /_us/ {
    proxy_pass https://collect.usherstats.com/;
    proxy_ssl_server_name on;
    proxy_set_header Host collect.usherstats.com;
    proxy_set_header Cookie "";
    proxy_set_header x-usher-proxy "sk_live_…";
    proxy_set_header x-usher-client-ip $remote_addr;
    proxy_set_header x-usher-client-country $geoip2_country;
    proxy_set_header x-usher-client-asn $geoip2_asn;
    proxy_set_header x-usher-client-as-org $geoip2_as_org;
}
```

If Nginx is itself behind a proxy or load balancer, use the visitor's address it forwards (for example with the
`realip` module) rather than the load balancer's.

### Caddy

With a GeoIP plugin that sets the visitor's country and network as placeholders (named here
`{geoip.country}`, `{geoip.asn}` and `{geoip.as_org}`; use your plugin's names):

```caddy
handle_path /_us/* {
    reverse_proxy https://collect.usherstats.com {
        header_up Host collect.usherstats.com
        header_up -Cookie
        header_up x-usher-proxy {env.USHERSTATS_SECRET}
        header_up x-usher-client-ip {remote_host}
        header_up x-usher-client-country {geoip.country}
        header_up x-usher-client-asn {geoip.asn}
        header_up x-usher-client-as-org {geoip.as_org}
    }
}
```

Both examples drop your site's cookies on the way: UsherStats needs none of them. (The one exception is the cookie
that marks your own team's browsers as internal; see [Concepts](concepts.md#internal-traffic). The Worker SDK forwards
that one alone.)

## The Worker SDK

For a site served by a Cloudflare Worker, wrap your handler:

```js
import { withUsherStats } from '@usherstats/worker';

const site = async (request) => new Response('<!doctype html><html><head><title>Home</title></head><body>Hello</body></html>', {
  headers: { 'content-type': 'text/html; charset=utf-8' },
});

export default {
  fetch: withUsherStats(site, { pageType: (path) => (path.startsWith('/blog/') ? 'article' : 'page') }),
};
```

Put the server secret in the Worker secret `USHERSTATS_SECRET` and the public key in `USHERSTATS_SITE_KEY`. The SDK:

- serves the snippet and its reports from `/_us/` on your hostname, with the visitor's network details, and adds the
  snippet to your HTML pages;
- records every request your Worker answers, people and crawlers alike, and sends the records in batches after the
  response, so no page waits for UsherStats;
- runs your site's [bot protection](bot-protection.md) before your code.

If UsherStats is slow or unreachable, your site is served as if it were not there. The options, and how bot protection
decides, are on the [Bot protection](bot-protection.md#setting-it-up-in-a-cloudflare-worker) page.

## Proxy mode

If your site is not on Cloudflare Workers, or you would rather not change it, point a hostname at UsherStats and it
sits in front of your server, with analytics and bot protection and no code. See [Proxy mode](proxy-mode.md).

## Checking the install

The site's Install page shows when the last pageview and the last server record arrived. Requests your server or proxy
records count towards your plan's server requests; pageviews count towards its human pageviews (see
[Billing and plans](billing-and-plans.md)).
