vTilt
Why vTiltHow It WorksFeaturesFAQDocs
Docs / Reverse proxy
Quick startEvent forwarding
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceGoogle Ads
Client setup
CursorClaude DesktopVS CodeCodex
Realtime
Debug ViewRealtime Dashboard
Integration guides
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
SDK
Browser SDK
InstallScript bundlesEvent trackingAutocaptureIdentify & aliasWeb VitalsSession recordingChat widgetFeature readinessRemote configurationReverse proxyDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdown

Documentation

vTilt
Quick startEvent forwarding

MCP server

Realtime

Debug ViewRealtime Dashboard

Integration guides

SDK

InstallScript bundlesEvent trackingAutocaptureIdentify & aliasWeb VitalsSession recordingChat widgetFeature readinessRemote configurationReverse proxyDebug logging
DocsBrowser SDKReverse proxy

Reverse proxy

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

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
PreviousRemote configurationBrowser SDKNextDebug loggingBrowser SDK

On this page

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