vTilt
By roleFoundersKnow what to fix firstMarketersSee what happens after the clickCMOsSee what your budget really buys
By businessSaaSTurn signups into active usersEcommerceSee why shoppers abandon cartsAgenciesProve your work to clientsEnterpriseOne customer record for every team
MeasureWeb AnalyticsFind where buyers leaveSession ReplaySee why they left, not just that they didAdsKnow what paid visitors do nextIntegrationsOne snippet feeds your stack
ThinkPeopleOne profile per customerCustomer MemoryvTilt remembers every visitorSite KnowledgeAnswers and audits from your own pages
ActAsk AIAnswers from real behaviorAI ChatChat that knows the visitorEmail CampaignsEmail from real visits
PricingWhy vTiltDocs
Docs / Reverse proxy
Getting Started
OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes
Guides
Event forwardingReverse proxyRealtime dashboardSite knowledge
Frontend frameworks
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus
Backend frameworks
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails
Backend languages
PythonPHPRubyElixirGoJava.NET / C#Rust
Stack guides
Vue + PHP
API Reference
Browser SDK
Script bundlesAutocaptureWeb VitalsSession recordingChat widgetFeature readinessRemote configurationDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdownGlobal properties & opt-outError tracking
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceSite knowledgeGoogle AdsMeta Ads
Client setup
CursorClaude DesktopVS CodeCodex

Getting Started

OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes

Guides

Event forwardingReverse proxyRealtime dashboardSite knowledge

API Reference

MCP server

On this page

On this page

  • Why use a reverse proxy?
  • SDK endpoints
  • SDK configuration
  • Next.js rewrites
  • Nginx
  • Cloudflare Workers
  • Script tag with proxy
DocsGuidesReverse proxy

Reverse proxy

Last updated July 22, 2026

Route SDK requests through your own domain to avoid ad blockers and keep data collection first-party.

Route SDK requests through your own domain to avoid ad blockers and keep data collection first-party. When a reverse proxy is configured, all requests appear to originate from your domain instead of a third-party analytics host.

#Why use a reverse proxy?

Ad blockers commonly block requests to known analytics domains and paths. A reverse proxy on your own domain makes SDK traffic indistinguishable from your regular API calls.

  • Ad blocker avoidance — requests go through your domain, not a third-party analytics host.
  • First-party cookies — cookies are set on your domain, improving identification accuracy.
  • Privacy compliance — data stays within your infrastructure boundary.

Note

Modern versions of the SDK authenticate with the x-api-key request header rather than a ?token= query parameter, so the URL stays clean and is less likely to match the standard filter rules. The server still accepts the legacy ?token= query parameter for backward compatibility, so older SDK builds keep working unchanged. The only path that still uses ?token= is navigator.sendBeacon on page unload, because beacons cannot carry custom headers.

#SDK endpoints

The SDK uses these short paths to minimise payload size and avoid ad-blocker pattern matching. Your reverse proxy must forward all of them — including the x-api-key request header — to your vTilt instance.

PathPurposeMethod
/api/eEvent ingestion (pageviews, custom events, identify).POST
/api/dRemote configuration.GET
/api/sSession recording snapshots.POST
/gt/*Google Tag gateway (gtag.js, collect, conversion) when destinations use proxied mode.GET / POST
/api/chat/*Chat widget (settings, messages, channels).GET / POST

#SDK configuration

Set api_host to your proxy domain so all SDK traffic routes through it.

vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://your-domain.com',
})
typescript

#Next.js rewrites

Add these rewrites to your next.config.js to proxy vTilt requests through your Next.js app.

/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      { source: '/api/e', destination: 'https://www.vtilt.com/api/e' },
      { source: '/api/d', destination: 'https://www.vtilt.com/api/d' },
      { source: '/api/s', destination: 'https://www.vtilt.com/api/s' },
      { source: '/gt/:path*', destination: 'https://www.vtilt.com/gt/:path*' },
      { source: '/api/chat/:path*', destination: 'https://www.vtilt.com/api/chat/:path*' },
    ]
  },
}

module.exports = nextConfig
javascript

#Nginx

Add location blocks to forward each path to your vTilt instance.

location /api/e {
    proxy_pass https://www.vtilt.com/api/e;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

location /api/d {
    proxy_pass https://www.vtilt.com/api/d;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

location /api/s {
    proxy_pass https://www.vtilt.com/api/s;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

location /gt/ {
    proxy_pass https://www.vtilt.com/gt/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

location /api/chat/ {
    proxy_pass https://www.vtilt.com/api/chat/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}
nginx

#Cloudflare Workers

Deploy a Cloudflare Worker on your domain to proxy SDK requests.

const VTILT_HOST = 'https://www.vtilt.com'

export default {
  async fetch(request) {
    const url = new URL(request.url)

    if (
      url.pathname.startsWith('/api/e') ||
      url.pathname.startsWith('/api/d') ||
      url.pathname.startsWith('/api/s') ||
      url.pathname.startsWith('/gt/') ||
      url.pathname.startsWith('/api/chat/')
    ) {
      const target = new URL(url.pathname + url.search, VTILT_HOST)
      const proxyReq = new Request(target, {
        method: request.method,
        headers: request.headers,
        body: request.body,
      })
      proxyReq.headers.set(
        'X-Forwarded-For',
        request.headers.get('CF-Connecting-IP') || '',
      )
      return fetch(proxyReq)
    }

    return fetch(request)
  },
}
javascript

#Script tag with proxy

When using the script tag snippet, set data-host to your proxy domain.

<script
  data-token="YOUR_PROJECT_TOKEN"
  data-host="https://your-domain.com"
  src="https://your-domain.com/dist/array.js"
></script>
html
PreviousEvent forwardingGuidesNextRealtime dashboardGuides