MenuCloudflare Worker

Proxy guides

Cloudflare Worker

One Cloudflare Worker serves the ProductLense script from your domain and, on the Scale plan, forwards events with the visitor's real IP address.

If your domain is on Cloudflare, one Worker handles both requests. It caches the script at the edge and reads the visitor's address from CF-Connecting-IP, which Cloudflare sets and visitors cannot forge.

The Worker

src/index.ts
interface Env {
  PL_API_KEY?: string;
}

type Event = Record<string, unknown>;

export default {
  async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(req.url);

    if (url.pathname === '/js/a.js') {
      const cache = caches.default;
      const hit = await cache.match(req);
      if (hit) return hit;
      const res = await fetch('https://api.productlense.app/a.js');
      if (res.ok) ctx.waitUntil(cache.put(req, res.clone()));
      return res;
    }

    if (url.pathname === '/m/api/e' && req.method === 'POST' && env.PL_API_KEY) {
      const text = await req.text();
      if (text.length > 8192) return new Response(null, { status: 413 });
      let body: { b?: Event[] } & Event;
      try {
        body = JSON.parse(text);
      } catch {
        return new Response(null, { status: 400 });
      }
      const extra = { ip: req.headers.get('CF-Connecting-IP') ?? '', ua: req.headers.get('User-Agent') ?? '' };
      const out = body.b ? { ...body, b: body.b.map((e) => ({ ...e, ...extra })) } : { ...body, ...extra };
      const r = await fetch('https://api.productlense.app/api/v1/events', {
        method: 'POST',
        headers: { Authorization: `Bearer ${env.PL_API_KEY}`, 'Content-Type': 'application/json' },
        body: JSON.stringify(out),
      });
      return new Response(null, { status: r.status });
    }

    return new Response('Not found', { status: 404 });
  },
};
wrangler.toml
name = "productlense-proxy"
main = "src/index.ts"
compatibility_date = "2026-10-01"
routes = [
  { pattern = "example.com/js/a.js", zone_name = "example.com" },
  { pattern = "example.com/m/api/e", zone_name = "example.com" },
]

The script response keeps ProductLense's Cache-Control: public, max-age=86400, so the edge cache refreshes at least once a day.

Use only the script part (any plan)

Without PL_API_KEY the Worker serves only the script. Point the tag at it and send events straight to ProductLense:

HTML
<script defer data-site="SITE_ID" data-api="https://api.productlense.app" src="/js/a.js"></script>

Forward events too (Scale plan)

  1. Create an API key with the events scope (see Authentication and API keys).
  2. Store it as a secret: npx wrangler secret put PL_API_KEY, then npx wrangler deploy.
  3. Change the tag to data-api="https://example.com/m".

Check it works

  1. Open https://example.com/js/a.js: it returns JavaScript, and a second load shows cf-cache-status: HIT.
  2. Load a page with the developer tools open. The Network tab shows a POST to api/e with status 204.
  3. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds, with your real country.

    ↑ ↓ to move, Enter to open