Skip to content

Serve analytics from your own domain

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 first.

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.

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).

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.

proxy.ts
import { NextResponse, type NextRequest } from "next/server";
export function proxy(request: NextRequest) {
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 } });
}
export const config = { matcher: "/nv/:path*" };

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

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.

src/index.js
export default {
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 });
},
};

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:

HeaderValue
x-nordva-proxy-keyYour proxy key. Required
x-nordva-client-ipThe visitor’s IP address (not your server’s). Used for rate limiting only
x-nordva-countryTwo-letter country code, if your host knows it
x-nordva-regionRegion code (ISO 3166-2, the part after the dash), optional
x-nordva-cityCity, URL-encoded, optional
x-nordva-lat, x-nordva-lonLatitude 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.

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

<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.)

  • Cookies and tracking modes work as before: the script sets them on your domain either way. The Cookie Policy 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.