First-Party Tracker Proxy
Serve the FullVision tracker through your own domain to bypass URL-based ad-blockers and use first-party HttpOnly cookies
Reverse-proxy the tracker through your own domain. Same data, your domain — bypasses URL-based ad-blockers and switches identity from a JS cookie to a first-party HttpOnly cookie that survives Safari ITP and cookie-clearing scripts.
Use it when a meaningful share of your visitors run an ad-blocker, or when your audience is heavily Safari and you are losing visitors between one visit and the next.
Who needs this
You probably do if you sell to a technical audience, if your Safari traffic shows far more first-time visitors than returning ones, or if your pageview counts read low against another analytics tool.
You probably don't if you are just getting started. The standard install collects the same data with one script tag and no infrastructure change. Come back here when you have a reason to.
Prerequisites
Your workspace must have cookie_domain set before first-party cookies can be scoped to your domain. Configure it in the dashboard's tracker settings if it's not already.
Step 1 — Pick a path prefix
Choose a path you'll mount under your own domain. Free-form, alphanumerics + - / _ / / only, max 64 chars.
Avoid predictable strings — /analytics, /stats, /tracker, /tracking get added to ad-blocker filter lists over time, which defeats the point.
Good prefixes: /insights, /api/m, /r, /i, /m, /p.
Set it in the dashboard. The dashboard persists it on the workspace; it's only read at verify time.
Step 2 — Add a reverse-proxy rule
Mount /<PREFIX>/* on your domain and forward to https://db.fullvision.io/*. Your proxy rule must do three things:
- Set
Host: db.fullvision.io. - Forward
X-Forwarded-For(so we can geo-locate the visitor). - Stamp
X-FV-Proxy: 1on every forwarded request. This header is what switches the request into first-party mode — ingest reads it per-request, so there is no separate "activate" step and no workspace-level flag. Requests that arrive without it are handled in plain direct mode.
Replace <PREFIX> with your chosen prefix (no leading slash on the placeholder — keep the existing slashes in each snippet).
{
"rewrites": [
{ "source": "/<PREFIX>/:path*", "destination": "https://db.fullvision.io/:path*" }
]
}The trailing slashes on the Nginx location and proxy_pass directives are required — they tell Nginx to strip the prefix before forwarding upstream.
A plain vercel.json rewrite cannot add the X-FV-Proxy request header (rewrites forward headers as-is; the headers config only sets response headers). The proxy will pass traffic through, but it stays in direct mode. To run first-party mode on Vercel, stamp the header in Edge Middleware (middleware.ts):
import { NextRequest, NextResponse } from "next/server";
export function middleware(req: NextRequest) {
const url = new URL(req.url);
const PREFIX = "/<PREFIX>";
if (!url.pathname.startsWith(PREFIX + "/")) return NextResponse.next();
const upstream = new URL(url.pathname.slice(PREFIX.length) + url.search, "https://db.fullvision.io");
const headers = new Headers(req.headers);
headers.set("Host", "db.fullvision.io");
headers.set("X-Forwarded-For", req.headers.get("x-forwarded-for") ?? req.ip ?? "");
headers.set("X-FV-Proxy", "1");
return fetch(upstream, { method: req.method, headers, body: req.body });
}
export const config = { matcher: "/<PREFIX>/:path*" };Step 3 — Update your tracker <script> tag
Swap the src from db.fullvision.io to your own domain plus the prefix. Keep the same publishable key (pk_…).
<!-- BEFORE -->
<script async src="https://db.fullvision.io/t.js?k=pk_..."></script>
<!-- AFTER -->
<script async src="https://<your-domain>/<PREFIX>/t.js?k=pk_..."></script>Step 4 — Verify in the dashboard
Click Verify in the dashboard. This is a pre-flight check — it calls GET /v1/proxy/verify against your prefix (using your cookie_domain + proxy_path_prefix) and confirms:
- The tracker script (
/t.js) is reachable through your prefix - The ingest endpoint is reachable through your prefix
- The query string survives the rewrite (auth depends on it)
- Your proxy forwards the visitor IP via
X-Forwarded-For
If anything fails, the dashboard surfaces the specific reason — see Troubleshooting below.
There is no separate "activate" step. First-party mode turns on the moment your proxy starts stamping X-FV-Proxy: 1 — it's decided per request, not by a workspace flag. Verify just confirms the chain is wired correctly before you point real traffic at it.
Step 5 — Confirm end-to-end
Load a real page on your site. The dashboard shows the new pageview within seconds. If you don't see it, jump to Troubleshooting.
What changes in first-party mode
- FullVision takes your visitor IP from
X-Forwarded-For[0](instead of the connection IP, which would be your proxy's egress). - FullVision resolves country server-side via geoip-lite, same as before.
POST /v1/ireturns200with a{ visitor_id }JSON body and sets an HttpOnlySet-Cookiescoped to yourcookie_domain(instead of returning204and letting the browser write a JS cookie).- Cross-pageview stickiness now survives Safari ITP, browser extensions that wipe
document.cookie, and the JS-clearing scripts some sites bundle accidentally.
Troubleshooting
Rollback
There's no dashboard switch to flip — first-party mode is per request, so rollback means making your proxy stop stamping X-FV-Proxy. Two ways:
- Drop the header — remove the
X-FV-Proxyline from your proxy rule. The next ingest hit falls back to direct mode immediately; the proxy keeps passing traffic through, just on the direct-mode path. - Revert the
<script src>— point it back atdb.fullvision.io/t.js?k=…so traffic skips the proxy chain entirely.
Either is sufficient on its own. Reverting the <script src> is the cleaner full rollback; dropping the header is the fastest if you can't redeploy the page.
