| name | cloudflare-zaraz |
| description | Server-side tag management for third-party tools with edge execution, privacy controls, and near-zero performance impact |
| version | 1.0.0 |
| tags | ["cloudflare","zaraz","tag-management","analytics","privacy","edge","performance","consent"] |
Cloudflare Zaraz
Server-side tag management platform that loads third-party tools at Cloudflare's edge, eliminating client-side performance impact while providing privacy controls and consent management.
Overview
Cloudflare Zaraz offloads third-party tools (analytics, advertising pixels, chatbots, marketing automation) to the cloud, executing at the edge rather than in the browser. This architecture delivers near-zero performance impact while enhancing security and privacy.
Key Benefits
- Performance: Load multiple tools with minimal client impact
- Privacy: Built-in IP anonymization, referrer hiding, user-agent trimming
- Security: Server-side execution reduces attack surface
- Consent: Integrated Consent Management Platform (CMP)
- Simplicity: No code changes needed for most integrations
Getting Started
Prerequisites
- Website proxied through Cloudflare (recommended)
- At least one tool configured in Zaraz
- Auto-inject script enabled (default)
Adding Third-Party Tools
- Navigate to Zaraz > Tools configuration in Cloudflare dashboard
- Select Third-party tools > Add new tool
- Browse the tools catalog and select your tool
- Configure tool-specific settings
- Set up actions with firing triggers
- Save configuration
Automatic Action Types
When tools support automatic actions:
- Pageviews: Track all page loads automatically
- Events: Monitor via
zaraz.track() Web API
- E-Commerce: Process transactions via
zaraz.ecommerce() API
Web API
The Zaraz Web API enables programmatic control from anywhere in the <body> tag.
zaraz.track()
Track custom events and user actions.
zaraz.track(eventName, [eventProperties])
zaraz.track("button_click")
zaraz.track("purchase", {
value: 200,
currency: "USD",
product_id: "SKU-123"
})
await zaraz.track("signup_complete", { plan: "premium" })
Common Use Cases:
- User actions (sign-ups, button clicks, purchases)
- Page interactions (widget loads, element impressions)
- Form submissions
Accessing Properties in Triggers:
{{ client.value }}
{{ client.`product_id` }} // Use backticks for special characters
zaraz.set()
Store variables for use across all events without repetition.
zaraz.set(key, value, [options])
zaraz.set("page_category", "blog", { scope: "page" })
zaraz.set("user_tier", "premium", { scope: "session" })
zaraz.set("user_id", "12345", { scope: "persist" })
zaraz.set("user_id", "12345")
zaraz.set("user_id", undefined)
Scope Options:
| Scope | Persistence | Use Case |
|---|
page | Current page only | Page-specific context |
session | Browser session | User session data |
persist | localStorage (default) | Cross-session identity |
zaraz.ecommerce()
Track e-commerce events with standardized data structure.
zaraz.ecommerce(eventName, eventProperties)
zaraz.ecommerce("Product Viewed", {
product_id: "507f1f77",
sku: "G-32",
name: "Monopoly",
price: 18.99,
category: "Games"
})
zaraz.ecommerce("Product Added", {
product_id: "507f1f77",
name: "Monopoly",
price: 18.99,
quantity: 1
})
zaraz.ecommerce("Order Completed", {
order_id: "50314b8e",
total: 27.50,
revenue: 25.00,
shipping: 3.00,
tax: 2.00,
currency: "USD",
products: [
{
product_id: "507f1f77",
name: "Monopoly",
price: 18.99,
quantity: 1
}
]
})
Supported Events (18 total):
- Product List Viewed, Products Searched
- Product Clicked, Product Viewed, Product Added, Product Removed
- Product Added to Wishlist
- Cart Viewed
- Checkout Started, Checkout Step Viewed, Checkout Step Completed
- Payment Info Entered, Shipping Info Entered
- Order Completed, Order Updated, Order Refunded, Order Cancelled
- Clicked Promotion, Viewed Promotion
Product Properties:
| Property | Description |
|---|
product_id | Unique identifier |
sku | Stock keeping unit |
name | Product name |
brand | Brand name |
category | Product category |
variant | Product variant |
price | Unit price |
quantity | Number of items |
coupon | Applied coupon |
position | List position |
Order Properties:
| Property | Description |
|---|
order_id | Order identifier |
checkout_id | Checkout identifier |
total | Total amount |
revenue | Revenue (excl. shipping/tax) |
shipping | Shipping cost |
tax | Tax amount |
discount | Discount applied |
currency | Currency code |
Compatible Tools:
- Google Analytics 4
- Google Analytics (Universal)
- Facebook Pixel
- Bing
- Pinterest Conversions API
- TikTok
- Amplitude
- Branch
SPA Support
For Single Page Applications, Zaraz automatically tracks virtual pageviews when URLs change (enable in Settings).
zaraz.spaPageview()
Triggers
Triggers define conditions for action execution.
Match Rule
Compare variables against match strings.
Variables Available:
- Event Name
- URL components (pathname, host, query)
- Cookies
- Device Properties
- Page Properties
Match Operations:
- Equals / Does not equal
- Contains / Does not contain
- Starts with / Ends with
- Matches regex / Does not match regex
- Greater than / Less than
Click Listener
Monitor clicks using CSS selectors or XPath.
#submit-button
.cta-button
button[type="submit"]
Parameters:
- Selector: CSS selector or XPath expression
- Wait for actions: Delay navigation (ms) to ensure requests complete
Form Submission
Track form submissions with optional validation.
#checkout-form
form[name="contact"]
Parameters:
- Selector: CSS selector for form
- Validate: Only trigger on valid submissions
Element Visibility
Fire when elements become visible in viewport.
#promo-banner
.video-player
Scroll Depth
Trigger at scroll thresholds.
100px /* Fixed pixel value */
50% /* Viewport percentage */
Timer
Execute after intervals.
Parameters:
- Interval: Milliseconds between executions
- Limit: Maximum executions (0 = unlimited)
Blocking Triggers
Prevent actions from firing under specific conditions.
Firing Trigger: Pageview
Blocking Trigger: URL contains "/admin"
Key Points:
- Inverse of firing triggers
- Zaraz script still loads
- Must apply to each action individually
Consent Management
GDPR and ePrivacy Directive compliance through built-in CMP.
Key Concepts
Purpose: Reason for loading a tool (e.g., "Analytics", "Advertising")
Consent: User permission to store/access cookies
Consent Storage
User preferences stored in first-party cookie:
{
"analytics": true,
"advertising": false,
"functional": true
}
Consent API
document.addEventListener("zarazConsentAPIReady", () => {
const analyticsConsent = zaraz.consent.get("analytics")
const allConsent = zaraz.consent.getAll()
zaraz.consent.set({
analytics: true,
advertising: false
})
zaraz.consent.setAll(true)
zaraz.consent.setAll(false)
zaraz.consent.modal = true
})
document.addEventListener("zarazConsentChoicesUpdated", () => {
console.log("User updated consent preferences")
})
zaraz.consent.sendQueuedEvents()
API Methods:
| Method | Description |
|---|
get(purposeId) | Get consent for specific purpose |
set(preferences) | Update consent for purposes |
getAll() | Get all consent statuses |
setAll(status) | Set all purposes |
getAllCheckboxes() | Get checkbox states |
setCheckboxes(states) | Update checkboxes |
setAllCheckboxes(state) | Set all checkboxes |
sendQueuedEvents() | Send blocked events after consent |
Properties:
| Property | Description |
|---|
modal | Show/hide consent modal |
purposes | Read-only purpose definitions |
APIReady | Boolean API availability |
Region-Based Consent
document.addEventListener("zarazConsentAPIReady", () => {
if (zaraz.consent.purposes.analytics.region === "EU") {
zaraz.consent.modal = true
} else {
zaraz.consent.setAll(true)
}
})
Properties Reference
Event Properties
| Property | Description |
|---|
| Event Name | Name from zaraz.track() |
| Track Property | Values from eventProperties or zaraz.set() |
Page Properties
| Property | Description |
|---|
| Page encoding | Document character encoding |
| Page referrer | Referring URL |
| Page title | Document title |
| Query param | Specific URL parameter |
| URL (various) | host, hostname, origin, pathname, port, protocol |
Device Properties
| Property | Description |
|---|
| Browser engine/name/version | Browser details |
| Device type | Desktop, mobile, tablet |
| Device CPU | Processor architecture |
| Language | Browser language |
| Resolution | Screen dimensions |
| Viewport | Browser viewport size |
| OS name/version | Operating system |
| User-agent | Full UA string |
| IP address | Visitor IP |
Location Properties
| Property | Description |
|---|
| City | Visitor city |
| Continent | Geographic continent |
| Country | Country code |
| EU | EU membership (1/0) |
| Region | Region name |
| Region code | ISO 3166-2 code |
| Timezone | Visitor timezone |
Miscellaneous
| Property | Description |
|---|
| Random number | Unique per request |
| Timestamp (ms/s) | Unix timestamp |
| Cookie | Browser cookie value |
Data Layer Compatibility
Migrate from Google Tag Manager without code changes.
Enable Compatibility Mode
- Navigate to Zaraz > Settings
- Enable Data layer compatibility mode
How It Works
dataLayer.push({
event: 'purchase',
price: '24',
currency: 'USD'
})
zaraz.track('purchase', { price: '24', currency: 'USD' })
Note: E-commerce mapping not supported via dataLayer. Use zaraz.ecommerce() instead.
Context Enricher
Enrich event data using Cloudflare Workers.
Basic Structure
export default {
async fetch(request) {
const { system, client } = await request.json()
const weather = await fetch('https://api.weather.com/...')
client.weather = await weather.json()
for (const key in client) {
if (typeof client[key] === 'string' && client[key].includes('@')) {
client[key] = '[REDACTED]'
}
}
return new Response(JSON.stringify({ system, client }))
}
}
Setup
- Create Worker in Cloudflare dashboard or via Wrangler
- Navigate to Zaraz > Settings
- Select your Worker as Context Enricher
Settings Reference
Workflow
| Setting | Description |
|---|
| Real-time | Changes publish immediately |
| Preview & Publish | Test before deployment |
Web API
| Setting | Description |
|---|
| Debug Key | Enable Debug Mode |
| E-commerce tracking | Enable zaraz.ecommerce() |
Compatibility
| Setting | Description |
|---|
| Data layer mode | GTM dataLayer.push() support |
| SPA support | Virtual pageviews on URL change |
Privacy
| Setting | Description |
|---|
| Remove query params | Strip URL parameters |
| Trim IP addresses | Remove before sending to tools |
| Hide user-agent | Sanitize sensitive details |
| Hide referrer | Hide external referrers |
| Cookie domain | Custom cookie domain |
Injection
| Setting | Description |
|---|
| Auto-inject | Load Zaraz automatically |
| Iframe injection | Inject into iframes |
Endpoints
| Setting | Description |
|---|
| Custom paths | Custom script pathnames |
Advanced
| Setting | Description |
|---|
| Bot threshold | Block suspected bot traffic |
| Context Enricher | Worker for data enrichment |
| Logpush | Export logs (Enterprise) |
Supported Tools
Analytics
- Amplitude
- Google Analytics (Universal)
- Google Analytics 4
- Mixpanel
- Segment
- Snowplow
- Pod Sights
Advertising
- Bing
- Facebook Pixel
- Floodlight
- Google Ads
- LinkedIn Insight
- Outbrain
- Pinterest / Pinterest Conversions API
- Quora
- Reddit
- Snapchat
- Taboola
- Tatari
- TikTok
- Twitter Pixel
Marketing
- Branch
- HubSpot
- Impact Radius
Recruiting
- Indeed
- iHire
- Upward
- ZipRecruiter
Custom Integrations
- Custom HTML
- Custom Image
- HTTP Request
Monitoring
Metrics Available
| Metric | Description |
|---|
| Loads | Zaraz script loads |
| Events | Tracked events (pageview, custom, ecommerce) |
| Triggers | Trigger activations |
| Actions | Action executions |
| Server-side requests | HTTP status codes from third-party tools |
Debug Mode
- Set Debug Key in Zaraz Settings
- Access via browser console
- View real-time event flow
Best Practices
Performance
- Use native integrations over Custom HTML
- Enable SPA support for single-page applications
- Use blocking triggers to prevent unnecessary actions
- Minimize Custom HTML - runs client-side
Privacy
- Enable IP trimming for GDPR compliance
- Configure consent purposes before enabling tools
- Use Context Enricher to redact sensitive data
- Hide referrers for cross-domain tracking protection
Migration from GTM
- Don't use GTM with Zaraz - loses optimization benefits
- Enable data layer compatibility for gradual migration
- Convert to native integrations over time
- Use zaraz.ecommerce() instead of dataLayer for e-commerce
Debugging
if (typeof zaraz !== 'undefined') {
console.log('Zaraz loaded successfully')
}
Common Issues
| Issue | Solution |
|---|
zaraz is not defined | Verify domain is proxied, Auto Injection enabled |
| Browser extension can't detect tools | Use Debug Mode (server-side execution) |
| E-commerce returns undefined | Enable E-commerce tracking in Settings |
| Demographics missing in GA | Use "Anonymize IP" instead of "Hide IP" |
Integration Examples
React E-commerce
import { useEffect } from 'react'
function ProductPage({ product }) {
useEffect(() => {
if (typeof zaraz !== 'undefined') {
zaraz.ecommerce('Product Viewed', {
product_id: product.id,
name: product.name,
price: product.price,
category: product.category
})
}
}, [product])
const handleAddToCart = () => {
if (typeof zaraz !== 'undefined') {
zaraz.ecommerce('Product Added', {
product_id: product.id,
name: product.name,
price: product.price,
quantity: 1
})
}
}
return (
<div>
<h1>{product.name}</h1>
<button onClick={handleAddToCart}>Add to Cart</>
)
}
Next.js with Consent
import { useEffect, useState } from 'react'
export function ConsentBanner() {
const [showBanner, setShowBanner] = useState(false)
useEffect(() => {
const handleReady = () => {
const allConsent = zaraz.consent.getAll()
if (Object.keys(allConsent).length === 0) {
setShowBanner(true)
}
}
document.addEventListener('zarazConsentAPIReady', handleReady)
return () => document.removeEventListener('zarazConsentAPIReady', handleReady)
}, [])
const acceptAll = () => {
zaraz.consent.setAll(true)
setShowBanner(false)
}
const rejectAll = () => {
zaraz.consent.setAll(false)
setShowBanner()
}
(!showBanner)
(
)
}
Vue.js Event Tracking
<template>
<button @click="trackClick">{{ label }}</button>
</template>
<script setup>
import { onMounted } from 'vue'
const props = defineProps({
label: String,
eventName: String,
eventProps: Object
})
onMounted(() => {
if (typeof zaraz !== 'undefined') {
zaraz.set('component_loaded', props.label, { scope: 'page' })
}
})
function trackClick() {
if (typeof zaraz !== 'undefined') {
zaraz.track(props.eventName, props.eventProps)
}
}
</script>
Checkout Flow
zaraz.ecommerce('Checkout Started', {
checkout_id: 'CHK-123',
value: 50.00,
currency: 'USD',
products: cartItems
})
zaraz.ecommerce('Shipping Info Entered', {
checkout_id: 'CHK-123',
shipping_method: 'standard'
})
zaraz.ecommerce('Payment Info Entered', {
checkout_id: 'CHK-123',
payment_method: 'credit_card'
})
zaraz.ecommerce('Order Completed', {
order_id: 'ORD-456',
checkout_id: 'CHK-123',
total: 53.00,
revenue: 50.00,
shipping: 3.00,
tax: 0,
currency: 'USD',
products: cartItems
})
Resources