Documentation

Self-serve proxy (ad-blocker bypass)

Ad blockers block requests to known analytics domains, which can hide up to ~30% of real traffic. Serving the tracker script and the collect endpoint from your own domain makes both requests first-party, so blocklists targeting didthey.click no longer apply.

This is the recommended setup

Proxying isn't a fallback for edge cases — it's how we recommend everyone install didthey.click. No SDK changes are needed: the script accepts a `data-endpoint` attribute pointing at any URL, and everything else works the same.

How it works

You proxy two paths on your domain to didthey.click:

  • GET /dtc/tracker.jshttps://www.didthey.click/sdk/tracker.js
  • POST /dtc/collecthttps://www.didthey.click/api/collect

Then update your snippet to load the script from your own domain and point events at your proxied collect path:

Proxied snippet

<script
	src="https://yoursite.com/dtc/tracker.js"
	data-website-id="YOUR_WEBSITE_ID"
	data-endpoint="https://yoursite.com/dtc/collect"
></script>

data-endpoint is required here

Without it, the script derives the endpoint from its own page origin by appending `/api/collect`, which would collide with your site's own `/api` routes.
  • /dtc/ is a suggestion — any neutral prefix works. Avoid words like analytics, track or stats in the path: ad blockers also match URL patterns, not just domains.
  • Your proxy must forward the visitor’s IP in X-Forwarded-For, otherwise geo/country resolution silently reports your proxy’s location instead of the visitor’s. Vercel, Netlify and Caddy do this by default; nginx needs it set explicitly (see below).

Vercel

vercel.json

{
	"rewrites": [
		{ "source": "/dtc/tracker.js", "destination": "https://www.didthey.click/sdk/tracker.js" },
		{ "source": "/dtc/collect", "destination": "https://www.didthey.click/api/collect" }
	]
}

Netlify

Add to public/_redirects (or _redirects in your publish directory):

_redirects

/dtc/tracker.js  https://www.didthey.click/sdk/tracker.js  200
/dtc/collect     https://www.didthey.click/api/collect     200

The 200 status makes it a proxy rewrite rather than a redirect — redirects would drop the POST body and re-expose the didthey.click domain to blockers.

Caddy

Caddyfile

yoursite.com {
	handle /dtc/tracker.js {
		rewrite * /sdk/tracker.js
		reverse_proxy https://www.didthey.click {
			header_up Host www.didthey.click
		}
	}
	handle /dtc/collect {
		rewrite * /api/collect
		reverse_proxy https://www.didthey.click {
			header_up Host www.didthey.click
		}
	}
}

nginx

nginx.conf

location = /dtc/tracker.js {
	proxy_pass https://www.didthey.click/sdk/tracker.js;
	proxy_set_header Host www.didthey.click;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_ssl_server_name on;
}

location = /dtc/collect {
	proxy_pass https://www.didthey.click/api/collect;
	proxy_set_header Host www.didthey.click;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_ssl_server_name on;
}

SvelteKit / Next.js (app-level proxy)

If you can’t touch the platform config, a tiny pass-through route works too. SvelteKit example, src/routes/dtc/collect/+server.ts:

src/routes/dtc/collect/+server.ts

export async function POST({ request, getClientAddress }) {
	const body = await request.arrayBuffer();
	return fetch('https://www.didthey.click/api/collect', {
		method: 'POST',
		headers: {
			'content-type': request.headers.get('content-type') ?? 'text/plain',
			'user-agent': request.headers.get('user-agent') ?? '',
			'x-forwarded-for': getClientAddress()
		},
		body
	});
}

Forwarding user-agent and x-forwarded-for matters: they drive device breakdowns, bot detection and geo.

Verifying it works

  1. Load your site with the new snippet and an ad blocker (e.g. uBlock Origin) enabled.
  2. In devtools → Network, confirm /dtc/tracker.js loads and /dtc/collect returns 200.
  3. Check the didthey.click dashboard: events should arrive with the correct country (not your hosting provider’s), which confirms the IP is forwarded properly.