Proxying the script
Serve Sift from your own domain so blockers and strict CSP do not drop events.
Some ad blockers and DNS filters block known analytics hosts. If that is costing you data, serve the script and the event endpoint from your own domain.
Sift supports this with no server changes: the script reads data-api and sends
events there instead.
What you are proxying
Two paths:
| Path | Upstream |
|---|---|
/stats/js/sift.js | https://sift.agentonboard.xyz/js/sift.js |
/stats/api/event | https://sift.agentonboard.xyz/api/event |
The names are yours to choose. Keep them stable, because the snippet embeds them.
The snippet
<script
defer
data-domain="example.com"
data-api="/stats/api/event"
src="/stats/js/sift.js"
></script>Without data-api, the script derives the endpoint from its own src origin and
posts to /api/event on your domain. Setting it explicitly is clearer and
survives a path rename.
Cloudflare Worker
export default {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/stats/js/sift.js") {
const upstream = await fetch("https://sift.agentonboard.xyz/js/sift.js", {
cf: { cacheTtl: 86400 },
});
return new Response(upstream.body, {
headers: {
"content-type": "application/javascript; charset=utf-8",
"cache-control": "public, max-age=86400",
},
});
}
if (url.pathname === "/stats/api/event" && request.method === "POST") {
// Forward the visitor's identity inputs. Without them every visitor
// looks like the proxy.
const headers = new Headers({ "content-type": "text/plain" });
const ip = request.headers.get("cf-connecting-ip");
const ua = request.headers.get("user-agent");
if (ip) headers.set("cf-connecting-ip", ip);
if (ua) headers.set("user-agent", ua);
return fetch("https://sift.agentonboard.xyz/api/event", {
method: "POST",
body: request.body,
headers,
});
}
return new Response("Not found", { status: 404 });
},
};Add a route for example.com/stats/* to this worker.
Next.js
const nextConfig = {
async rewrites() {
return [
{ source: "/stats/js/sift.js", destination: "https://sift.agentonboard.xyz/js/sift.js" },
{ source: "/stats/api/event", destination: "https://sift.agentonboard.xyz/api/event" },
];
},
};Rewrites forward the method and body, so the POST survives.
Nginx
location = /stats/js/sift.js {
proxy_pass https://sift.agentonboard.xyz/js/sift.js;
proxy_set_header Host sift.agentonboard.xyz;
}
location = /stats/api/event {
proxy_pass https://sift.agentonboard.xyz/api/event;
proxy_set_header Host sift.agentonboard.xyz;
proxy_set_header Content-Type text/plain;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}Rules the proxy must respect
- Do not parse the body. Events are sent as
text/plainwith a JSON string inside. Pass it through untouched. - Forward the visitor's IP. Sift reads
cf-connecting-ipfirst, thenx-forwarded-for. If you strip both, every visitor on that path looks like your proxy's IP and unique counts collapse. - Forward the user agent. Bot filtering and browser reports depend on it.
- Keep the
POSTmethod.sendBeaconsendsPOST. A redirect toGETloses the event.
That second point is the one that quietly breaks proxies: the script still
sends, the endpoint still answers 202, and visitors silently merge into one.
Verifying
Load a page, watch the network panel, and confirm:
- The request goes to your domain, not
sift.agentonboard.xyz. - The response is
202. - The dashboard shows a new pageview, from a new visitor.
If the pageview arrives but visitors stay at 1, check the IP headers.