Stickybeak Docs Dashboard Get early access

Backends

Cloudflare Workers

Cloudflare Workers analytics with Stickybeak starts by adding its script to your rendered pages; this guide shows you where it belongs and how to track page views, goals and purchase revenue.

Before you start

Your domain needs to be on Cloudflare with its DNS records proxied (the orange cloud), so requests pass through the Worker. You'll also need Wrangler.

Add the script

  1. Write the Worker

    It passes each request through to your origin, then appends the tag to <head> on HTML responses only:

    export default {
      async fetch(request, env) {
        const response = await fetch(request);
        const type = response.headers.get("content-type") || "";
        if (!env.STICKYBEAK_SITE_ID || !type.includes("text/html")) return response;
    
        const tag = `<script
      defer
      data-site="${env.STICKYBEAK_SITE_ID}"
      data-domain="example.com"
      src="https://app.usestickybeak.com/_cfw/s.js"
    ></script>`;
    
        return new HTMLRewriter()
          .on("head", { element(head) { head.append(tag, { html: true }); } })
          .transform(response);
      },
    };
  2. Route it to your site

    Set the site ID as a variable, and run the Worker on your production hostname only. Copy the ID from Settings in the dashboard.

    {
      "name": "stickybeak-inject",
      "main": "src/index.js",
      "compatibility_date": "2026-09-01",
      "routes": [{ "pattern": "example.com/*", "zone_name": "example.com" }],
      "vars": { "STICKYBEAK_SITE_ID": "YOUR_SITE_ID" }
    }
  3. Deploy

    npx wrangler deploy

Already serving from a Worker?

If your site is a Worker (with static assets, say) or a Pages project, transform your own response the same way instead of calling fetch(request).

export async function onRequest({ next, env }) {
  const response = await next();
  if (!env.STICKYBEAK_SITE_ID || !(response.headers.get("content-type") || "").includes("text/html")) return response;
  return new HTMLRewriter()
    .on("head", { element(head) { head.append(`<script defer data-site="${env.STICKYBEAK_SITE_ID}" data-domain="example.com" src="https://app.usestickybeak.com/_cfw/s.js"></script>`, { html: true }); } })
    .transform(response);
}

Check it's working

Deploy first, then run curl -s https://example.com/ | grep _cfw.

See Verify your install.

Track goals

Click goals are a data-goal attribute on the element, so the same Worker can add them. Match elements with a CSS selector:

return new HTMLRewriter()
  .on("head", { element(head) { head.append(tag, { html: true }); } })
  .on('a[href="/pricing"]', { element(a) { a.setAttribute("data-goal", "view_pricing"); } })
  .on("form#signup button[type=submit]", { element(b) { b.setAttribute("data-goal", "signup_submit"); } })
  .transform(response);

See Custom goals.

Track purchases

Render window.stickybeak("purchase", { revenue, currency, order_id }) on the checkout confirmation page.

See Revenue.

If it isn't working

  • The DNS record must be proxied, the route must match the hostname, and only text/html responses are rewritten.

See Troubleshooting.