First-Party Tracking with Cloudflare Workers
Set up a first-party proxy using Cloudflare Workers. This guide is for users on shared hosting (Hostinger, GoDaddy, Bluehost, etc.) who cannot set up server-side proxies.
Important Limitation: Firefox ETP Not Bypassed
This guide sets up a custom subdomain (z.yourdomain.com). While this bypasses some ad blockers and Safari ITP, it does NOT bypass Firefox Enhanced Tracking Protection (ETP) because subdomains are still considered different origins.
If you need Firefox ETP compatibility, use one of these same-domain proxy options instead:
- Cloudflare Pages Functions - If you're on Cloudflare Pages
- Vercel - If you're on Vercel
- Netlify - If you're on Netlify
- Next.js - If you're using Next.js
These use /api/_z/ paths which are same-origin and truly bypass all tracking protection.
Difficulty: Easy - Takes about 10 minutes. No coding experience required. Just copy and paste.
Subdomain Naming
If you proceed with this approach, use z.yourdomain.com (not analytics.*, metrics.*, tracking.*, or stats.* which are additionally blocked by heuristic filters).
Before You Start
Make sure you have:
- A Cloudflare account (free tier works perfectly) - Sign up here
- Your domain added to Cloudflare (see Step 2)
- Your Zenovay tracking code (found in Dashboard → Your Site → Settings)
Your Tracking Code Format
Your tracking code looks like: ZV_XXXXXXXXXX
- Starts with
ZV_ - Followed by 10 characters (letters and numbers)
- CASE-SENSITIVE - copy it exactly
Example: ZV_Q8U0GYD70WR
Why Cloudflare Workers?
Cloudflare Workers run on Cloudflare's global edge network (300+ data centers). This means:
- Fast: Sub-50ms response times worldwide
- Free: 100,000 requests/day on the free plan (more than enough for most sites)
- Simple: No server to manage
- Reliable: 99.99% uptime
Step 1: Create a Cloudflare Account
If you already have a Cloudflare account, skip to Step 2.
- Go to cloudflare.com
- Click Sign Up (top right corner)
- Enter your email address
- Create a password (at least 8 characters)
- Click Create Account
- Check your email and click the verification link
Email Not Arriving? Check your spam folder. The email comes from [email protected].
Step 2: Add Your Domain to Cloudflare
This step is required. Without adding your domain to Cloudflare, you cannot create a custom subdomain like z.yourdomain.com.
- Log in to your Cloudflare dashboard
- Click Add a Site (big blue button)
- Enter your domain name (e.g.,
yourdomain.com)- Don't include
www.orhttps:// - Just the domain:
yourdomain.com
- Don't include
- Click Continue
- Select the Free plan and click Continue
- Cloudflare will scan your existing DNS records - click Continue
- Cloudflare shows you two nameservers like:
bella.ns.cloudflare.comtroy.ns.cloudflare.com
- Copy these nameservers - you'll need them in the next step
Update Your Domain's Nameservers
Now you need to point your domain to Cloudflare:
- Log in to your domain registrar (where you bought your domain):
- GoDaddy: godaddy.com
- Namecheap: namecheap.com
- Google Domains: domains.google.com
- Hostinger: hostinger.com
- Find your domain's DNS or Nameserver settings
- Replace the existing nameservers with Cloudflare's nameservers
- Save the changes
Nameserver changes take time. It can take anywhere from 5 minutes to 48 hours for the changes to propagate. Usually it's done within 30 minutes.
Cloudflare will email you when your domain is active.
Using shared hosting? You can still add your domain to Cloudflare. Your existing hosting will continue to work - Cloudflare just becomes a proxy in front of it. Your website will still work normally.
Step 3: Create the Worker
- In the Cloudflare dashboard sidebar, click Workers & Pages
- Click the blue Create button
- Click Create Worker
- Give your worker a name:
zenovay-proxy- Use only lowercase letters, numbers, and hyphens
- No spaces allowed
- Click Deploy
You'll see a success message. Now we need to add the proxy code.
Step 4: Add the Proxy Code
Don't skip any steps! Copy the code exactly as shown. One missing character will break everything.
- After deploying, click Edit code (or go to your Worker and click "Edit code")
- You'll see a code editor with some default code
- Select ALL the existing code (Cmd+A on Mac, Ctrl+A on Windows)
- Delete it (press Delete or Backspace)
- Copy and paste the following code:
// Zenovay First-Party Proxy Worker
// This proxies tracking requests through your domain to bypass ad blockers
export default {
async fetch(request) {
const url = new URL(request.url);
// Get the real visitor IP address (for accurate geolocation)
const clientIP = request.headers.get('CF-Connecting-IP') || '';
// Determine the target URL based on the request path
let targetUrl;
if (url.pathname === '/script.js' || url.pathname === '/z.js') {
// Script requests go to the regular endpoint
targetUrl = `https://api.zenovay.com/z.js${url.search}`;
} else {
// All other requests (events, settings) go through first-party endpoint
targetUrl = `https://api.zenovay.com/fp${url.pathname}${url.search}`;
}
// Copy the original request headers
const headers = new Headers(request.headers);
// IMPORTANT: Forward the real visitor IP for accurate geolocation
headers.set('X-Zenovay-Real-IP', clientIP);
// Remove the Host header (we're proxying to a different domain)
headers.delete('Host');
// Handle CORS preflight requests
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',
},
});
}
try {
// Forward the request to Zenovay API
const response = await fetch(targetUrl, {
method: request.method,
headers: headers,
body: request.body,
});
// Add CORS headers to the response
const responseHeaders = new Headers(response.headers);
responseHeaders.set('Access-Control-Allow-Origin', '*');
// Return the response to the visitor's browser
return new Response(response.body, {
status: response.status,
headers: responseHeaders,
});
} catch (error) {
// If something goes wrong, return an error
return new Response(JSON.stringify({ error: 'Proxy error' }), {
status: 502,
headers: { 'Content-Type': 'application/json' },
});
}
}
}- Click Save and Deploy (top right corner)
- Wait for the "Worker deployed successfully" message
Code copied successfully? You should see "Worker deployed successfully" at the top. If you see any errors, make sure you copied ALL the code including the opening export default { and closing }.
Step 5: Add Your Custom Domain
CRITICAL: You MUST use z.yourdomain.com
Firefox and Edge automatically block these subdomains:
- ❌
analytics.yourdomain.com- BLOCKED - ❌
metrics.yourdomain.com- BLOCKED - ❌
tracking.yourdomain.com- BLOCKED - ❌
stats.yourdomain.com- BLOCKED - ❌
telemetry.yourdomain.com- BLOCKED - ❌
pixel.yourdomain.com- BLOCKED
The ONLY recommended subdomain is: z.yourdomain.com ✅
This is a neutral, single-letter subdomain that browsers don't block.
Now let's connect your Worker to your domain:
- Go to your Worker in the Cloudflare dashboard
- Click the Settings tab (not "Triggers" - it moved to Settings)
- Scroll down to find Domains & Routes section
- Click Add → Custom Domain
- Enter your subdomain:
z.yourdomain.com- Replace
yourdomain.comwith your actual domain - Keep the
z.prefix exactly as shown
- Replace
- Click Add Custom Domain
- Cloudflare will automatically:
- Create a DNS record for
z.yourdomain.com - Provision an SSL certificate (HTTPS)
- This takes 1-2 minutes
- Create a DNS record for
Wait for the green checkmark. Your custom domain will show "Initializing" then change to "Active" with a green checkmark. Don't proceed until you see the green checkmark.
Step 6: Test Your Worker
Before adding the tracking script, let's make sure the Worker is working:
- Open a new browser tab
- Go to:
https://z.yourdomain.com/script.js- Replace
yourdomain.comwith your actual domain
- Replace
- You should see JavaScript code (a bunch of minified code)
See JavaScript code? Your Worker is working correctly. Proceed to Step 7.
See an error or blank page?
- Check that your custom domain shows "Active" in Cloudflare
- Wait 2-3 minutes and try again
- Make sure you typed the URL correctly
Step 7: Add the Tracking Script to Your Website
IMPORTANT: You must use the inline script method below.
The simple <script src="..."> method does NOT work reliably with Cloudflare Workers because:
- Cloudflare Rocket Loader can interfere with script loading
- The config must be set BEFORE the script loads
Use the inline script method shown below.
Add this code to your website's <head> section:
<script data-cfasync="false">
window.ZENOVAY_TRACKER_CONFIG = {
trackingCode: 'YOUR_TRACKING_CODE',
apiUrl: 'https://z.yourdomain.com',
settingsEndpoint: '/settings'
};
var script = document.createElement('script');
script.src = 'https://z.yourdomain.com/script.js';
script.defer = true;
document.head.appendChild(script);
</script>What to Replace:
| Placeholder | Replace With | Example |
|---|---|---|
YOUR_TRACKING_CODE | Your tracking code from Zenovay dashboard | ZV_Q8U0GYD70WR |
z.yourdomain.com (appears 2 times) | Your actual custom domain | z.mywebsite.com |
Replace BOTH occurrences of z.yourdomain.com! There are two places in the code where you need to put your domain.
Example with Real Values:
<script data-cfasync="false">
window.ZENOVAY_TRACKER_CONFIG = {
trackingCode: 'ZV_Q8U0GYD70WR',
apiUrl: 'https://z.mywebsite.com',
settingsEndpoint: '/settings'
};
var script = document.createElement('script');
script.src = 'https://z.mywebsite.com/script.js';
script.defer = true;
document.head.appendChild(script);
</script>Where to Add This Code:
- WordPress: Use a plugin like "Insert Headers and Footers" or add to your theme's
header.php - Wix: Settings → Custom Code → Head Code
- Squarespace: Settings → Advanced → Code Injection → Header
- Shopify: Online Store → Themes → Edit Code →
theme.liquid(in the<head>section) - HTML files: Between the
<head>and</head>tags - Any CMS: Look for "Custom Code" or "Header Scripts" in your settings
Step 8: Verify It's Working
Check 1: Network Tab (Any Browser)
- Open your website in Chrome, Firefox, or Edge
- Press F12 (or Cmd+Option+I on Mac) to open DevTools
- Click the Network tab
- Refresh your website (Cmd+R or Ctrl+R)
- In the filter/search box, type
script.js - Look for a request to
z.yourdomain.com/script.js
What you should see:
- Status: 200 ✅
- Domain:
z.yourdomain.com(your domain, not api.zenovay.com)
Check 2: Firefox Strict Mode (Most Important!)
Firefox has the strictest tracking protection. If it works in Firefox, it works everywhere.
- Open Firefox browser
- Click the menu (☰) → Settings
- Click Privacy & Security in the left sidebar
- Under "Enhanced Tracking Protection", select Strict
- Close Settings and visit your website
- Open DevTools (F12) → Network tab
- Refresh the page
- Search for
script.js- it should load with status 200
Works in Firefox Strict mode? Congratulations! Your first-party tracking is set up correctly and will work in ALL browsers.
Check 3: Zenovay Dashboard
- Go to app.zenovay.com and log in
- Click on your website
- Visit your website in another tab
- Within 1-2 minutes, you should see the visit appear in your dashboard
Final Checklist
Before you're done, verify ALL of these:
- Worker deployed at
z.yourdomain.com(responding with JavaScript code) - Custom domain uses
z.prefix (NOTanalytics.,metrics., etc.) - Script tag includes
data-cfasync="false"attribute -
window.ZENOVAY_TRACKER_CONFIGis set with correcttrackingCode - Both URLs in the script use your custom domain
z.yourdomain.com - Tested in Firefox with Enhanced Tracking Protection set to Strict
- Visits appearing in Zenovay dashboard
Troubleshooting
Worker Returns 522 or 524 Error
Cause: Timeout connecting to Zenovay API.
Solution:
- Check that
api.zenovay.comis accessible (visit it in your browser) - Verify there are no typos in the proxy code
- Try redeploying the worker (Edit code → Save and Deploy)
Custom Domain Shows SSL Error
Cause: SSL certificate not yet provisioned.
Solution: Wait 1-5 minutes. Cloudflare automatically provisions SSL certificates. If it takes longer than 10 minutes, check that your domain's nameservers are correctly pointing to Cloudflare.
Script Loads But No Data in Dashboard
Cause: Usually a tracking code mismatch.
Solution:
- Open your browser's DevTools Console (F12 → Console tab)
- Look for any red error messages
- Verify your
trackingCodematches exactly what's in your Zenovay dashboard (case-sensitive!) - Make sure the website domain is registered in Zenovay
"Script executing" But Nothing Happens
Cause: The apiUrl in your config is wrong.
Solution:
Make sure your window.ZENOVAY_TRACKER_CONFIG has the correct apiUrl:
apiUrl: 'https://z.yourdomain.com', // Your actual custom domain
Geolocation Shows Wrong Location
Cause: Real IP not being forwarded to Zenovay.
Solution: Make sure your worker code includes this line:
headers.set('X-Zenovay-Real-IP', clientIP);
Firefox/Edge Still Blocking
Cause: You're using a blocked subdomain like analytics.* or metrics.*.
Solution:
Change your custom domain to z.yourdomain.com. You'll need to:
- Go to Workers & Pages → Your Worker → Settings
- Delete the old custom domain
- Add new custom domain:
z.yourdomain.com - Update both URLs in your tracking script
Advanced: Multiple Websites
If you have multiple websites, you can use one worker for all of them:
-
Add multiple custom domains to the same worker:
z.website1.comz.website2.comz.website3.com
-
Each website uses its own tracking code:
<script data-cfasync="false">
window.ZENOVAY_TRACKER_CONFIG = {
trackingCode: 'ZV_SITE1CODE',
apiUrl: 'https://z.website1.com',
settingsEndpoint: '/settings'
};
var s = document.createElement('script');
s.src = 'https://z.website1.com/script.js';
s.defer = true;
document.head.appendChild(s);
</script><script data-cfasync="false">
window.ZENOVAY_TRACKER_CONFIG = {
trackingCode: 'ZV_SITE2CODE',
apiUrl: 'https://z.website2.com',
settingsEndpoint: '/settings'
};
var s = document.createElement('script');
s.src = 'https://z.website2.com/script.js';
s.defer = true;
document.head.appendChild(s);
</script>Next Steps
- Custom Events - Track button clicks and form submissions
- Visitor Identification - Connect analytics to user accounts
- Troubleshooting - More help with common issues
Need Help?
If you're stuck:
- Double-check each step above
- Make sure you're using
z.yourdomain.com(notanalytics.*ormetrics.*) - Test in Firefox Strict mode
- Check the browser console for errors (F12 → Console tab)
Still stuck? Contact support at [email protected] with:
- Your domain name
- Screenshot of the Network tab
- Any error messages from the Console tab