First-party proxy
By default the browser talks to the 3x API host directly. A first-party proxy routes the same
requests through a path on your own domain, e.g. https://example.com/n3/….
Why
- Ad blockers block third-party analytics hosts; measured losses without a proxy are typically
5–25 % of visits. A path on your own domain is not on block lists (avoid words like
analytics,tracking,statsor the product name in the path –/n3is the default). - Visitor separation: sessions are stitched with a hash of IP and user agent (Cookieless sessions). A proxy that does not forward the visitor's IP makes everyone look like the proxy server. The 3x proxy protocol forwards the IP authenticated, so the API can trust it.
- CSP: everything becomes same-origin (
script-src 'self'; connect-src 'self').
The protocol
host maps 1:1 onto the API root, so a proxy at /n3 forwards /n3/v1/e to <api>/v1/e. Only four
path families are forwarded:
| Path | Method | Purpose |
|---|---|---|
/v1/e (and /v1/collect) | POST, OPTIONS | Event batches (text/plain JSON) |
/v1/c/{pk} | GET | Client configuration |
/v1/cb | POST | Conversion beacon |
/v1/s/{file} | GET | SDK scripts (cache them: public, max-age=3600, stale-while-revalidate=86400) |
For each forwarded request the proxy:
- removes
cookieandauthorization– the proxy never sends your site's cookies to 3x; - forwards
user-agent,origin,content-typeand the body unchanged; - adds
x-3x-client-ip(the visitor's IP: firstx-forwarded-forentry,x-real-ip,cf-connecting-ipor the socket address – whatever your platform guarantees),x-3x-client-country(x-vercel-ip-country,cf-ipcountry, if available) andx-3x-proxy-key(your proxy key, from server-side configuration).
The API trusts x-3x-client-* only when x-3x-proxy-key is a valid key with the scope
ingest:proxy for the project of the batch. A wrong or revoked key is rejected with 403, so a
misconfiguration is visible immediately rather than silently merging visitors.
The proxy key
Create it in Project → Settings → Keys → Proxy keys (POST /v1/projects/{project}/proxy-keys,
scope project:admin; or the MCP tool setup_generate_snippet with proxy: true). It is a secret
key that can do nothing except vouch for forwarded IPs of this one project. Keep it in your server's
environment (X3_PROXY_KEY), never in the browser. Roll it like any key (Authentication and keys).
Recipes
| Platform | Recipe |
|---|---|
| Next.js (App or Pages Router), any Fetch-API runtime | Next.js route handler |
| Cloudflare (any site behind Cloudflare, Webflow, static hosting) | Cloudflare Worker |
| Nginx, Apache-fronted servers | Nginx |
| Netlify | Netlify edge function |
| WordPress | WordPress REST route |
| No server access at all | CNAME subdomain |
What does not work
Plain rewrites (vercel.json rewrites, next.config.js rewrites, Netlify redirects with status
- cannot add headers. They are fine for the script (
/v1/s/*), but event POSTs through them
arrive with the CDN's IP – on a Vercel-hosted API the forwarded IP is overwritten – and visitors get merged. The dashboard warns when more than 30 % of a day's sessions share one visitor hash.