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.
- Go to Workers & Pages in your Cloudflare dashboard
- Click Create Worker
- Paste the Cloudflare Worker code (see below)
- Save and deploy
- Go to Triggers → Add Route
- Add route:
yoursite.com/t/* - In Lytical, go to Settings → Site Settings → First-Party Tracking
- 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>
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
- Open your browser's Network tab (F12 → Network)
- Visit your website
- Look for requests to your proxy path (e.g.,
/t/events) - Verify they return 200 status
- 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.