Clicky Analytics Anti‑Adblock Tracking on Cloudflare Pages

(Using Cloudflare Pages Functions as a Reverse Proxy)


Overview

Standard analytics trackers are blocked by ~20% of users due to ad blockers.

Clicky provides an anti-adblock proxy method, which works by:

To ad blockers, this appears as first-party traffic. This guide shows how to implement it using Cloudflare Pages Functions, with no nginx, Workers dashboard setup, or DNS changes.

Tip:

Using a first-party proxy method like this makes your analytics more resilient to ad blockers and privacy-focused browsers.


How This Works (Architecture)

Browser → static.getclicky.com ❌ (blocked)

We will proxy it like this:

Browser → customdomain.com/<CLICKY_JS_PATH>
         → Cloudflare Pages Function
         → static.getclicky.com
         → Response returned
Browser → customdomain.com/<CLICKY_BEACON_PATH>
         → Cloudflare Pages Function
         → in.getclicky.com
         → Response returned

Phase 1 — Prerequisites

1. Cloudflare Pages Project

wrangler pages deploy .

2. Custom Domain Connected

customdomain.com

3. Clicky Anti-Adblock Paths


Phase 2 — Project Structure

your-project/
│
├── functions/
│
└── (your other files)

Phase 3 — Create the JavaScript Proxy

/functions/<CLICKY_JS_PATH>.js
/functions/0a3b1c9d8e7f.js
export async function onRequest(context) {
  const beaconPath = "/<CLICKY_BEACON_PATH>";

  const upstreamURL = `https://static.getclicky.com/js?in=${encodeURIComponent(beaconPath)}`;

  const response = await fetch(upstreamURL, {
    method: "GET",
    headers: { "Host": "static.getclicky.com" }
  });

  return new Response(response.body, {
    status: response.status,
    headers: response.headers
  });
}

Phase 4 — Create the Beacon Proxy

/functions/<CLICKY_BEACON_PATH>.js
/functions/9f8e7d6c5b4a.js
export async function onRequest(context) {
  const request = context.request;

  const cookieHeader = request.headers.get("cookie") || "";
  let filteredCookies = "";

  const ignoreMatch = cookieHeader.match(/_cky_ignore=[^;]+/);
  const osaMatch = cookieHeader.match(/_cky_osa=[^;]+/);

  if (ignoreMatch) filteredCookies += ignoreMatch[0];
  if (osaMatch) filteredCookies += filteredCookies ? "; " + osaMatch[0] : osaMatch[0];

  const url = new URL(request.url);
  const upstreamURL = `https://in.getclicky.com/in.php${url.search}`;

  const response = await fetch(upstreamURL, {
    method: request.method,
    headers: {
      "Host": "in.getclicky.com",
      "X-Forwarded-For": request.headers.get("CF-Connecting-IP"),
      "X-Forwarded-Proto": "https",
      "X-Forwarded-Host": url.hostname,
      "Cookie": filteredCookies
    }
  });

  return new Response(response.body, { status: response.status, headers: response.headers });
}

Phase 5 — Deploy

wrangler pages deploy .

Phase 6 — Verification