Filling a gap in server-side tag management

Cloudflare Zaraz was built to move data collection from the browser to Cloudflare's global network, letting marketers measure user journeys without the performance hit of traditional client-side pixels. But one capability was missing: running custom JavaScript as part of the data processing pipeline before events reach third-party vendors.

In practice, analytics data rarely arrives in the exact shape that tagging plans specify. Analysts define custom attributes such as an internal page name, but the raw page context doesn't always contain clean, ready-to-send values. Other tag management systems address this by letting users inject client-side JavaScript functions that clean PII, transform product arrays, or fetch enrichment data from APIs before payloads are dispatched.

Zaraz needed the equivalent — but had to stay true to its server-side, performance-first design. The solution, Worker Variables, uses Cloudflare Workers as the execution environment for user-defined code that runs as part of the Zaraz Worker itself.

How Worker Variables work internally

Requests to sites proxied by Cloudflare pass through firewall, DDoS mitigation, and caching layers before hitting the origin, and that pipeline also includes First-Party Workers. Cloudflare Zaraz is one such worker, built so that certain variables can be replaced either by hardcoded strings or by live content fetched at runtime.

Standard Zaraz variables hold static values set from the dashboard — a site name, a secret key, or other reusable text. Worker Variables extend the model: instead of substituting the variable with a stored string, the Zaraz Worker invokes a Cloudflare Worker you've deployed and uses the response as the variable's value. The invocation happens internally through Dynamic Dispatch.

Dynamic data collection with Zaraz Worker Variables

In the Zaraz Worker, the call is set up through a binding declared in wrangler.toml, and the variable-handling code dispatches to your worker using that binding. The cost is a subrequest from the global network rather than a full extra HTTP roundtrip from the visitor's browser.

What the architecture buys you

Running your variables as Workers rather than as client-side JavaScript functions provides several practical advantages:

  • Context is included automatically. Zaraz passes the current visitor session context — track properties, device attributes, cookies — as input to your function. That context includes everything your Web API instrumentation has captured for the current user.
  • Faster execution. Because there's no roundtrip to an HTTP endpoint from the browser, the data surgery happens on Cloudflare's network. The client never makes an extra request for the enrichment step.
  • An isolated sandbox. Your code runs in the Worker runtime, not in the visitor's browser. It has no DOM access and cannot crash or slow down the user experience. Sensitive API keys and data stay server-side.

Worker Variables also pair with the Custom HTML tool to shift client-side JavaScript entirely off the browser. AJAX requests can be made from the network edge, resource-intensive manipulation runs in a Worker, and only the finished result travels to the client.

Porting a GTM custom variable

A typical use case is translating existing client-side tag manager logic to Zaraz. Consider a Google Tag Manager Custom JavaScript Variable that sums the prices of products in an ecommerce purchase:

BLOG-1850 Embedded Image - BoukLJ

The obvious challenge is that this references a Data Layer Variable using GTM's {{...}} syntax. In Zaraz, the equivalent is a track property — a custom attribute passed via the Web API. Assuming a GTM variable such as "DLV - Ecommerce - Purchase - Products" maps to a Zaraz track property named products, the translated Worker Variable needs to pull that value from the incoming context.

The Worker receives the context in two objects: client, containing track properties for the current visitor, and system, with generic device attributes. From there, the product aggregation loop stays nearly identical, but the result is formatted as a proper HTTP Response object returned by the Worker.

export default {
  async fetch(request, env) {
    // $1 Parse the Zaraz Context object
    const { system, client } = await request.json();

    // $2 Get a reference to the products track property
    const products = client.products;

    // $3 Calculate the sum
    const prices = products.map(p => p.price).join();

    return new Response(prices);
  },
};

Enriching data from an external API

Another useful pattern is enriching an event with data that only an external system can supply. For instance, syncing online behavior with offline records requires a common identifier, and a CRM might be the system of record that maps a session cookie to a CRM ID.

A Worker Variable for this scenario reads the session identifier from the visitor's cookie, calls an endpoint like https://example.com/api/getUserIdFromCookie to exchange it for the CRM ID, and returns that ID — all before Zaraz sends the event downstream. The enrichment arrives complete, without client-side exposure of the CRM API credentials.

export default {
  async fetch(request, env) {
    // Parse the Zaraz Context object
    const { system, client } = await request.json();

    // Get the value of the cookie "login-cookie"
    const cookieValue = system.cookies["login-cookie"];

    const userId = await fetch("https://example.com/api/getUserIdFromCookie", {
      method: POST,
      body: cookieValue,
    });

    return new Response(userId);
  },
};

Setup and configuration

Worker Variables are available on all accounts with a paid Workers plan, starting at $5/month. The first step is deploying a Worker.

Through the dashboard:

  1. Log in and navigate to Workers.
  2. Select Create a Service, name it, and choose the HTTP Handler starter.
  3. Click Create Service, then Quick Edit.

With Wrangler, from an empty directory:

$ npx wrangler init my-project
$ cd my-project

Run the development server:

$ npx wrangler dev

Then start writing your Worker code:

// my-project/index.js || my-project/index.ts
export default {
 async fetch(request) {
   // Parse the Zaraz Context object
   const { system, client } = await request.json();

   return new Response("Hello World!");
 },
};

To wire up the Worker Variable itself:

  1. Log in to the Cloudflare dashboard.
  2. Go to Zaraz > Tools configuration > Variables.
  3. Select Create variable.
  4. Give the variable a name, choose Worker as the type, and select the deployed Worker.
BLOG-1850 Embedded Image - pHewKv

Once saved, the variable is picked up wherever standard variables appear:

  1. Open Zaraz > Tools configuration > Tools and click Edit next to a configured tool.
  2. Select or add an action.
  3. Click the plus sign next to a text field and choose your Worker Variable from the list.