Skip to content
English
  • There are no suggestions because the search field is empty.

First-Party Tracking Setup

 

Route Lytical tracking through your own domain to avoid ad blockers and improve data accuracy.

Why First-Party Tracking? By default, Lytical loads from lytcdn.com and sends data to app.lytical.ai. Ad blockers may block these third-party domains. First-party tracking routes everything through your own domain, making it appear as your own traffic.

Benefits

  • Better data accuracy - Avoids ad blockers and privacy tools
  • Improved privacy - Data stays first-party
  • Longer cookie life - Safari ITP doesn't limit first-party cookies

Setup Options

Option 1: Cloudflare Workers (Recommended)

If you use Cloudflare, this is the easiest option.

  1. Go to Workers & Pages in your Cloudflare dashboard
  2. Click Create Worker
  3. Paste the Cloudflare Worker code (see below)
  4. Save and deploy
  5. Go to Triggers → Add Route
  6. Add route: yoursite.com/t/*
  7. In Lytical, go to Settings → Site Settings → First-Party Tracking
  8. Enter your endpoint: https://yoursite.com/t/events

Update your script tag:

<!-- Before -->
<script async src="https://lytcdn.com/lyt.js?site=YOUR_TOKEN"></script>

<!-- After -->
<script async src="https://yoursite.com/t/lyt.js?site=YOUR_TOKEN"></script>
View Cloudflare Worker Code
const LYTICAL_SCRIPT_URL = 'https://lytcdn.com/lyt.js';
const LYTICAL_API_BASE = 'https://app.lytical.ai/i/v1';

export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const path = url.pathname;

if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Max-Age': '86400',
},
});
}

try {
// Proxy the tracker script
if (path === '/t/lyt.js' || path === '/t/lyt.min.js') {
const scriptUrl = new URL(LYTICAL_SCRIPT_URL);
scriptUrl.search = url.search;
const response = await fetch(scriptUrl.toString());
let scriptContent = await response.text();
scriptContent = scriptContent.replace(
'https://app.lytical.ai/i/v1/config/',
url.origin + '/t/config/'
);
return new Response(scriptContent, {
headers: {
'Content-Type': 'application/javascript',
'Cache-Control': 'public, max-age=3600',
'Access-Control-Allow-Origin': '*',
},
});
}

// Proxy config requests
if (path.startsWith('/t/config/')) {
const token = path.replace('/t/config/', '');
const response = await fetch(`${LYTICAL_API_BASE}/config/${token}`);
let config = await response.json();
config.ingest = url.origin + '/t/events';
return new Response(JSON.stringify(config), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=300',
'Access-Control-Allow-Origin': '*',
},
});
}

// Proxy event ingestion
if (path === '/t/events') {
const response = await fetch(`${LYTICAL_API_BASE}/events`, {
method: request.method,
headers: {
'Content-Type': 'application/json',
'X-Forwarded-For': request.headers.get('CF-Connecting-IP') || '',
},
body: request.body,
});
return new Response(response.body, {
status: response.status,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
});
}

return new Response('Not Found', { status: 404 });
} catch (error) {
return new Response('Proxy Error', { status: 500 });
}
},
};

Option 2: Vercel Rewrites

Add to your vercel.json:

{
"rewrites": [
{
"source": "/t/lyt.js",
"destination": "https://lytcdn.com/lyt.js"
},
{
"source": "/t/config/:token",
"destination": "https://app.lytical.ai/i/v1/config/:token"
},
{
"source": "/t/events",
"destination": "https://app.lytical.ai/i/v1/events"
}
]
}

Option 3: Netlify Redirects

Add to your netlify.toml:

[[redirects]]
from = "/t/lyt.js"
to = "https://lytcdn.com/lyt.js"
status = 200

[[redirects]]
from = "/t/config/*"
to = "https://app.lytical.ai/i/v1/config/:splat"
status = 200

[[redirects]]
from = "/t/events"
to = "https://app.lytical.ai/i/v1/events"
status = 200

Option 4: nginx Proxy

location /t/lyt.js {
proxy_pass https://lytcdn.com/lyt.js;
proxy_set_header Host lytcdn.com;
proxy_ssl_server_name on;
}

location /t/config/ {
proxy_pass https://app.lytical.ai/i/v1/config/;
proxy_set_header Host app.lytical.ai;
proxy_ssl_server_name on;
}

location /t/events {
proxy_pass https://app.lytical.ai/i/v1/events;
proxy_set_header Host app.lytical.ai;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
}

Option 5: Apache Proxy

ProxyPass /t/lyt.js https://lytcdn.com/lyt.js
ProxyPassReverse /t/lyt.js https://lytcdn.com/lyt.js

ProxyPass /t/config/ https://app.lytical.ai/i/v1/config/
ProxyPassReverse /t/config/ https://app.lytical.ai/i/v1/config/

ProxyPass /t/events https://app.lytical.ai/i/v1/events
ProxyPassReverse /t/events https://app.lytical.ai/i/v1/events

Testing Your Setup

  1. Open your browser's Network tab (F12 → Network)
  2. Visit your website
  3. Look for requests to your proxy path (e.g., /t/events)
  4. Verify they return 200 status
  5. Check your Lytical dashboard for incoming events

Troubleshooting

Events not showing up?

  • Check that your proxy returns 200 status
  • Verify the custom endpoint URL is set correctly in Lytical Settings
  • Make sure the script tag uses your proxy URL

CORS errors?

  • Ensure your proxy adds appropriate CORS headers
  • The Cloudflare Worker template handles this automatically

Script not loading?

  • Check that the proxy path is correct
  • Verify your CDN/server is properly forwarding the query string (site token)

Need help? Contact support@lytical.ai or visit our help center.