# The tracking script

Add page views, Web Vitals, and JavaScript errors to your site.

One script records page views, [Web Vitals](https://docs.kogut.dev/web-apps/web-vitals/), and
[JavaScript errors](https://docs.kogut.dev/web-apps/javascript-errors/), without cookies or browser storage. See
[Data kogut collects](https://docs.kogut.dev/web-apps/privacy/).

## Add a site

1. Open **Settings → Sites → Add site** and enter `acme.com`.
2. Copy the script tag into the `<head>` of every page:

   ```html
   <script defer src="https://kogut.acme.com/b.js" data-site="acme.com"></script>
   ```

3. Open your site and check **Analytics** for the visit.

Use the same tag on subdomains such as `www.acme.com` and `shop.acme.com`. kogut rejects
pages outside the site's domain and subdomains.

**Next.js**

Render this component in your root layout:

```tsx title="app/layout.tsx"
import Script from "next/script";

<Script
	src="https://kogut.acme.com/b.js"
	data-site="acme.com"
	strategy="afterInteractive"
/>;
```

**Astro**

Use `is:inline` to keep Astro from bundling the script:

```astro title="src/layouts/Base.astro"
<head>
	<script is:inline defer src="https://kogut.acme.com/b.js" data-site="acme.com"></script>
</head>
```

[Client-side navigation](https://docs.kogut.dev/web-apps/single-page-apps/) needs no extra setup. For an app on
Vercel, Netlify, or Cloudflare Pages, see [Hosted apps](https://docs.kogut.dev/web-apps/hosted-apps/).

## Analytics history

**Analytics** covers up to one year (`1y`). Days older than the retained raw page views
come from daily rollups. A metric is left out when its data is not kept for the whole
range, such as JavaScript errors in `1y` with the default retention. A metric has no
comparison when the previous period extends beyond its retention.

## Link the site to a service

To show errors on a service and use its traffic in incidents, add the site to that
service's [`sites`](https://docs.kogut.dev/reference/configuration/#service) list:

```toml
[[service]]
name = "website"
sites = ["acme.com"]
```

Create the site in the panel first. kogut links existing sites at startup and when the
file changes.

## Change the script's settings

To send events to another endpoint, add `data-api="https://kogut.acme.com/e"` to the tag.
Without it, events go to `/e` on the script's origin.

To test on local addresses, add `data-local`. The script otherwise skips `localhost`,
`127.*`, `0.0.0.0`, `[::1]`, `*.localhost`, and `file:` pages. The server still requires
an HTTP or HTTPS page URL on a registered domain.

Without `data-site` or a `data-website-id` (see the
[migration guides](https://docs.kogut.dev/coming-from/umami/)), the script does not track.

### The app version

To tell which build of your app each visitor runs, add `data-version` with your release:

```html
<script defer src="https://kogut.acme.com/b.js" data-site="acme.com" data-version="1.4.2"></script>
```

Page views, Web Vitals, and JavaScript errors then carry the version, and
[deploy reports](https://docs.kogut.dev/deploys/reports/#by-app-version) compare the deployed version with the
previous one. Set it at build time from your release tag or commit, such as
`git describe --tags`, and send the same version to the [Deploy API](https://docs.kogut.dev/deploys/api/). A visitor whose
browser still runs an older cached build reports that build's version.

A version has up to 64 characters: ASCII letters, digits, `.`, `_`, `+`, and `-`. kogut
stores events with a longer version or other characters, but without the version.

Each site keeps up to 20 versions. A new version takes the place of the one seen least
recently, but only after that one has had no events for an hour, so requests that make up
versions cannot push out the ones your visitors run. Events with a version that was not
kept are stored without one. The list starts empty when the server restarts.

## Exclude your own visits

Block the script in your browser. For uBlock Origin, add these lines under **My filters**:

```text
||kogut.acme.com/b.js
||kogut.acme.com/script.js
```

There is no per-visitor opt-out stored in the browser.

## Consent

If your site requires consent, load the script only after the visitor agrees:

```html
<script>
	function loadKogut() {
		const script = document.createElement("script");
		script.src = "https://kogut.acme.com/b.js";
		script.dataset.site = "acme.com";
		document.head.append(script);
	}
</script>
```

Call `loadKogut()` from your consent tool. Until then, the script sends no requests.

## Allow the script in your content security policy

Add kogut's origin to both directives:

```text
script-src 'self' https://kogut.acme.com;
connect-src 'self' https://kogut.acme.com;
```

## Serve the script through your site

To use your site's origin, proxy both `/b.js` and `/e`. With Caddy:

```caddy title="Caddyfile"
acme.com {
	handle /b.js {
		reverse_proxy https://kogut.acme.com {
			header_up Host kogut.acme.com
		}
	}
	handle /e {
		reverse_proxy https://kogut.acme.com {
			header_up Host kogut.acme.com
		}
	}
	# Keep your site's other handlers here.
}
```

Then use a relative script URL:

```html
<script defer src="/b.js" data-site="acme.com"></script>
```

If the proxy runs on another machine or in another container, add its address to
[`trusted_proxies`](https://docs.kogut.dev/reference/configuration/#server). kogut trusts loopback proxies
by default.

If **Analytics** warns that most visits come from one address, check proxy trust and
Docker port forwarding: kogut counts everyone as one visitor and applies one rate limit to
them all. When that address is private and sends `X-Forwarded-For`, such as a proxy on
Docker's bridge network, the **Overview** shows the `trusted_proxies` line to add. See
[Client addresses behind a proxy](https://docs.kogut.dev/installation/caddy/#client-addresses-behind-a-proxy).
