# Serve analytics from your own domain

> Pass the analytics script and its requests through your own domain, so ad blockers that block analytics hosts do not drop your visits.

Some ad blockers block requests to analytics hosts, so part of your visits never arrive. If your server passes the script and its requests through a path on your own domain (`/nv/…` below), they look like any other request to your site.

It takes three things: a **proxy key**, a few lines on your server, and a changed script tag. Set up [web analytics](/guides/web-analytics) first.

## Why the key

Through a proxy, every request reaches Launch from your server instead of the visitor's browser. Requests from servers are normally dropped as bots, and the visitor's country and city would be your server's. Your proxy therefore sends a key with each request, and passes on the visitor's IP address and location. With a valid key, Launch uses what your proxy forwarded. Without one, a request from a server is dropped, so nobody else can post visits as your proxy.

The IP address is used for rate limiting and, on computers, to recognise a browser that cleared its cookies (a keyed one-way code, never the address), and is never stored, as with the direct script. The location decides the map and, in the Auto tracking mode, whether the visitor needs to consent first. If your proxy forwards no location, the country counts as unknown: the visit is counted without a cookie until your consent banner says yes.

## 1. Create the key

In the dashboard: **Insights → Script and tracking mode → Serve it from your own domain → Create proxy key**. Or `POST /v1/analytics/proxy-key` with your secret key, or ask your AI agent (`create_analytics_proxy_key`). The key is shown once. Put it in your server's environment as `NORDVA_PROXY_KEY`.

A new key replaces the old one at once. Removing it stops visits through the proxy from counting (within a minute).

## 2. Add the proxy

### Next.js

On Vercel this passes on the visitor's location from the headers Vercel adds to every request, so there is nothing else to set. Next.js 16 calls the file `proxy.ts`; on Next.js 13 to 15, name it `middleware.ts` and the function `middleware`. If you already have one, add the `/nv` branch to it and `"/nv/:path*"` to its matcher.

```ts title="proxy.ts"

  const target = new URL(request.nextUrl.pathname.replace(/^\/nv/, ""), "https://e.nordva.dev");
  const headers = new Headers(request.headers);
  // Your site's cookies and credentials are not Launch's business.
  headers.delete("cookie");
  headers.delete("authorization");
  headers.set("x-nordva-proxy-key", process.env.NORDVA_PROXY_KEY ?? "");
  headers.set("x-nordva-client-ip", request.headers.get("x-real-ip") ?? "");
  // The visitor's location, from Vercel's headers (elsewhere, your host's).
  for (const [from, to] of [["country", "country"], ["country-region", "region"], ["city", "city"], ["latitude", "lat"], ["longitude", "lon"]]) {
    const value = request.headers.get(`x-vercel-ip-${from}`);
    if (value) headers.set(`x-nordva-${to}`, value);
  }
  return NextResponse.rewrite(target, { request: { headers } });
}

```

A plain `rewrites()` entry in `next.config` is not enough: it cannot add the key, so every visit would be dropped.

### Cloudflare Workers

If your site runs through Cloudflare, add a Worker on the route `example.com/nv/*` and a secret `NORDVA_PROXY_KEY` (`npx wrangler secret put NORDVA_PROXY_KEY`). Cloudflare knows each visitor's location, and the Worker passes it on.

```js title="src/index.js"

  async fetch(request, env) {
    const url = new URL(request.url);
    const target = new URL(url.pathname.replace(/^\/nv/, ""), "https://e.nordva.dev");
    const headers = new Headers(request.headers);
    headers.delete("cookie");
    headers.delete("authorization");
    headers.set("x-nordva-proxy-key", env.NORDVA_PROXY_KEY);
    headers.set("x-nordva-client-ip", request.headers.get("cf-connecting-ip") ?? "");
    const cf = request.cf ?? {};
    if (cf.country) headers.set("x-nordva-country", cf.country);
    if (cf.regionCode) headers.set("x-nordva-region", cf.regionCode);
    if (cf.city) headers.set("x-nordva-city", encodeURIComponent(cf.city));
    if (cf.latitude && cf.longitude) {
      headers.set("x-nordva-lat", String(cf.latitude));
      headers.set("x-nordva-lon", String(cf.longitude));
    }
    return fetch(target, { method: request.method, headers, body: request.body });
  },
};
```

### Any other server

Forward `GET /nv/v1/s.js` and `POST /nv/e` to the same paths without `/nv` on `https://e.nordva.dev`, with the request body and the browser's headers, except `cookie` and `authorization`. Add:

| Header | Value |
|---|---|
| `x-nordva-proxy-key` | Your proxy key. Required |
| `x-nordva-client-ip` | The visitor's IP address (not your server's). Used for rate limiting only |
| `x-nordva-country` | Two-letter country code, if your host knows it |
| `x-nordva-region` | Region code (ISO 3166-2, the part after the dash), optional |
| `x-nordva-city` | City, URL-encoded, optional |
| `x-nordva-lat`, `x-nordva-lon` | Latitude and longitude, optional (rounded to about 11 km when stored) |

Keep the `user-agent`, `accept-language`, `origin` and `sec-gpc` headers as the browser sent them: Launch reads the browser family from the first, drops requests without the second, checks the third against your domains, and respects the fourth.

## 3. Change the script tag

Load the script through your path and send its requests there with `data-api`:

```html
<script defer src="/nv/v1/s.js" data-site="ste_…" data-api="/nv"></script>
```

The dashboard shows this line with your site id once you have a key. Open your site, then check **Insights**: the setup card, or `get_analytics_status`, says when the first visit arrived. If none arrives: `/nv/v1/s.js` should load in your browser, and your server must have `NORDVA_PROXY_KEY` set to the key you created last. (`/nv/e` answers 204 even when a visit is refused, so a 204 alone proves only that the proxy reaches Launch.)

## What changes, and what does not

- Cookies and tracking modes work as before: the script sets them on your domain either way. The [Cookie Policy](https://launch.nordva.dev/cookies/#analytics-cookies) still lists them.
- Your server now handles one small request per page view.
- The map shows the location your proxy forwards. On Vercel and Cloudflare that is the visitor's; elsewhere it is whatever your host provides, or nothing.

Source: https://docs.nordva.dev/guides/analytics-proxy/
