BonicBD
Documentation

Server-Side GTM Setup Guide & Documentation

Use this guide to deploy BonicBD, configure tags, and validate cleaner attribution flows. If you need pricing or migration context first, compare the plans or contact the team.

What Is Server-Side GTM?

Server-side Google Tag Manager (sGTM) moves your tracking from the visitor's browser to a server you control. Instead of the browser sending events directly to Meta, Google, or TikTok, it sends them to your own subdomain — and your server forwards them to the destination platforms.

In practice this can improve three things: common browser-side blockers are less likely to intercept a first-party endpoint, cookies can often persist longer when they are managed server-side, and events can be enriched before they are forwarded onward.

Requirements

  • A domain you control (e.g., yourbrand.com)
  • Ability to create a DNS CNAME record on a subdomain
  • A Google Tag Manager account (free) — both Web and Server containers

1. Sign Up & Pick a Plan

Create an account at https://panel.bonicbd.com/user/signup. Pick a plan based on your monthly request volume — see pricing. The free trial is enough to validate the setup before you commit.

2. DNS Configuration (CNAME)

Pick a tracking subdomain like tracking.yourbrand.com, server.yourbrand.com, or gtm.yourbrand.com. Then add this single CNAME record at your DNS provider:

Type:   CNAME
Name:   tracking          (or server / gtm — your choice)
Value:  connect.bonicbd.com
TTL:    300               (or default — Auto on Cloudflare)
Important: No IP address is needed. Always use a CNAME pointing to connect.bonicbd.com — that way, infrastructure changes on our side never break your tracking. If you're on Cloudflare, set proxy status to DNS only (grey cloud) so SSL provisioning works.
Tip: Avoid generic names like track. or analytics. — some ad-blocker lists target them. tracking., server., gtm., ss. all work well.

3. Install the Snippets (auto-generated)

Once your CNAME is verified, the dashboard generates three ready-to-paste snippets for your container. You don't write them by hand — copy from Dashboard → Setup Guide and paste:

  1. Head code (Google Tag Manager loader) — paste at the very top of <head> on every page.
  2. Body noscript — paste right after the opening <body> tag.
  3. Integrated Tracker script — a separate first-party script that handles cookie keeping, click ID restoration, UTM capture, and event de-duplication.

The Head and Tracker scripts both load from your own tracking subdomain, which can improve resilience against common browser-side blocking patterns. The exact snippet URLs are auto-generated per container.

WordPress / Shopify / Woo: Use a header/footer plugin (Insert Headers and Footers, etc.) or your theme's custom code section. Custom sites: paste into your main layout template.

4. Web GTM & Server GTM Setup

Inside your Web GTM container add a Cookie Extender (Custom HTML on All Pages) and a 1st-Party Cookie variable for bbd_uid — both are pre-written in the dashboard. In your Server GTM container set the Server Container URL to your tracking subdomain and add the Event Data variable for external_id mapping. The Setup Guide gives you exact field values per container.

5. Shopify & WooCommerce Quick Notes

For detailed platform steps, see the Shopify vs WooCommerce server-side tracking guide. In short: use a header/footer plugin or your theme's custom code section to paste the three snippets, then add your Meta Pixel and CAPI credentials in the BonicBD dashboard. No backend code change is usually required for either platform.

Cookie Keeper can refresh server-set _fbp, _fbc, _ga, and click-ID cookies for longer retention windows. Exact duration still depends on browser rules, consent settings, and your final implementation.

Click ID Restorer

Captures fbclid, gclid, ttclid, msclkid, twclid, li_fat_id, epik, scid, _kx, and obclid on first hit. Stores them in cookies, localStorage, and IndexedDB so later conversion events can use those identifiers again when the setup is configured to do so.

Configure default consent state per region. EU traffic gets denied defaults until consent; non-EU traffic stays granted. Conversion modeling kicks in for denied-consent EU traffic. Toggle via the dashboard.

Bot Detection

We score every request and silently drop traffic from known bot user agents and headless browser fingerprints before it reaches your analytics pipeline. Reduces noise and protects your event quality.

User Data Variables for GTM

User Data Variables enable enhanced matching and conversion lift across Meta, Google, and other platforms. These variables collect user information (email, phone, address) from multiple sources with a priority system.

Available Variables

  • UserData-Email (em) - Email address
  • UserData-Phone (ph) - Phone number
  • UserData-Fname (fn) - First name
  • UserData-Lname (ln) - Last name
  • UserData-City (ct) - City
  • UserData-State (st) - State/Region
  • UserData-Zip (zp) - Postal code
  • UserData-Country - Country
  • UserData-Address - Complete address object

Data Source Priority

Each variable checks sources in this order:

  1. BBD Context - window.BBD_CONTEXT.user_data
  2. dataLayer - Enricher events and user_data objects
  3. Cookies - bbd_pii, _bbd_ppi, bbd_id_cache

Installation Steps

  1. Go to GTM → Variables → New
  2. Set Variable Type to Custom JavaScript
  3. Copy the function code for each variable from below
  4. Save with the exact variable name (e.g., UserData-Email)
  5. Repeat for all 9 variables

Variable Functions

UserData-Email (em)

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.em) return ctx.em;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_em) return e.bbd_em;
    if (e.user_data && e.user_data.em) return e.user_data.em;
    if (e.user_data && e.user_data.sha256_email_address) return e.user_data.sha256_email_address;
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.em) return decoded.em;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-Phone (ph)

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.ph) return ctx.ph;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_ph) return e.bbd_ph;
    if (e.user_data && e.user_data.ph) return e.user_data.ph;
    if (e.user_data && e.user_data.sha256_phone_number) return e.user_data.sha256_phone_number;
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.ph) return decoded.ph;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-Fname (fn - First Name)

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.fn) return ctx.fn;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_fn) return e.bbd_fn;
    if (e.user_data && e.user_data.fn) return e.user_data.fn;
    if (e.user_data && e.user_data.sha256_first_name) return e.user_data.sha256_first_name;
    if (e.user_data && e.user_data.address && e.user_data.address.sha256_first_name) {
      return e.user_data.address.sha256_first_name;
    }
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.fn) return decoded.fn;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-Lname (ln - Last Name)

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.ln) return ctx.ln;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_ln) return e.bbd_ln;
    if (e.user_data && e.user_data.ln) return e.user_data.ln;
    if (e.user_data && e.user_data.sha256_last_name) return e.user_data.sha256_last_name;
    if (e.user_data && e.user_data.address && e.user_data.address.sha256_last_name) {
      return e.user_data.address.sha256_last_name;
    }
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.ln) return decoded.ln;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-City (ct)

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.ct) return ctx.ct;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_ct) return e.bbd_ct;
    if (e.user_data && e.user_data.ct) return e.user_data.ct;
    if (e.user_data && e.user_data.address && e.user_data.address.city) {
      return e.user_data.address.city;
    }
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.ct) return decoded.ct;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-State (st)

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.st) return ctx.st;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_st) return e.bbd_st;
    if (e.user_data && e.user_data.st) return e.user_data.st;
    if (e.user_data && e.user_data.address && e.user_data.address.region) {
      return e.user_data.address.region;
    }
    if (e.user_data && e.user_data.address && e.user_data.address.state) {
      return e.user_data.address.state;
    }
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.st) return decoded.st;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-Zip (zp)

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.zp) return ctx.zp;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_zp) return e.bbd_zp;
    if (e.user_data && e.user_data.zp) return e.user_data.zp;
    if (e.user_data && e.user_data.address && e.user_data.address.postal_code) {
      return e.user_data.address.postal_code;
    }
    if (e.user_data && e.user_data.address && e.user_data.address.zip) {
      return e.user_data.address.zip;
    }
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.zp) return decoded.zp;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-Country

function() {
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx && ctx.country) return ctx.country;
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    if (e.bbd_country) return e.bbd_country;
    if (e.user_data && e.user_data.country) return e.user_data.country;
    if (e.user_data && e.user_data.address && e.user_data.address.country) {
      return e.user_data.address.country;
    }
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded && decoded.country) return decoded.country;
      }
    }
  } catch (e) {}
  
  return '';
}

UserData-Address (Complete Address Object)

function() {
  var address = {};
  
  // Priority 1: BBD Context
  var ctx = window.BBD_CONTEXT && window.BBD_CONTEXT.user_data;
  if (ctx) {
    if (ctx.ct) address.city = ctx.ct;
    if (ctx.st) address.region = ctx.st;
    if (ctx.st) address.state = ctx.st;
    if (ctx.zp) address.postal_code = ctx.zp;
    if (ctx.country) address.country = ctx.country;
    if (ctx.fn) address.sha256_first_name = ctx.fn;
    if (ctx.ln) address.sha256_last_name = ctx.ln;
  }
  
  // Priority 2: Enricher events + dataLayer
  var dl = window.dataLayer || [];
  for (var i = dl.length - 1; i >= 0; i--) {
    var e = dl[i];
    if (!e) continue;
    
    // Check bbd_* fields
    if (e.bbd_ct && !address.city) address.city = e.bbd_ct;
    if (e.bbd_st && !address.region) address.region = e.bbd_st;
    if (e.bbd_st && !address.state) address.state = e.bbd_st;
    if (e.bbd_zp && !address.postal_code) address.postal_code = e.bbd_zp;
    if (e.bbd_country && !address.country) address.country = e.bbd_country;
    if (e.bbd_fn && !address.sha256_first_name) address.sha256_first_name = e.bbd_fn;
    if (e.bbd_ln && !address.sha256_last_name) address.sha256_last_name = e.bbd_ln;
    
    // Check user_data.address
    if (e.user_data && e.user_data.address) {
      var addr = e.user_data.address;
      if (addr.city && !address.city) address.city = addr.city;
      if (addr.region && !address.region) address.region = addr.region;
      if (addr.state && !address.state) address.state = addr.state;
      if (addr.postal_code && !address.postal_code) address.postal_code = addr.postal_code;
      if (addr.country && !address.country) address.country = addr.country;
      if (addr.sha256_first_name && !address.sha256_first_name) address.sha256_first_name = addr.sha256_first_name;
      if (addr.sha256_last_name && !address.sha256_last_name) address.sha256_last_name = addr.sha256_last_name;
    }
  }
  
  // Priority 3: Cookie fallback
  try {
    var cookies = ['bbd_pii', '_bbd_ppi', 'bbd_id_cache'];
    for (var j = 0; j < cookies.length; j++) {
      var match = document.cookie.match(new RegExp('(^|;\\s*)' + cookies[j] + '=([^;]+)'));
      if (match) {
        var decoded = JSON.parse(atob(decodeURIComponent(match[2])));
        if (decoded) {
          if (decoded.ct && !address.city) address.city = decoded.ct;
          if (decoded.st && !address.region) address.region = decoded.st;
          if (decoded.st && !address.state) address.state = decoded.st;
          if (decoded.zp && !address.postal_code) address.postal_code = decoded.zp;
          if (decoded.country && !address.country) address.country = decoded.country;
          if (decoded.fn && !address.sha256_first_name) address.sha256_first_name = decoded.fn;
          if (decoded.ln && !address.sha256_last_name) address.sha256_last_name = decoded.ln;
        }
      }
    }
  } catch (e) {}
  
  // Return empty object if no address data
  return Object.keys(address).length > 0 ? address : {};
}

Field Mapping Reference

Variable BBD Context dataLayer bbd_* dataLayer user_data Cookie
UserData-Emailuser_data.embbd_emuser_data.em, user_data.sha256_email_addressem
UserData-Phoneuser_data.phbbd_phuser_data.ph, user_data.sha256_phone_numberph
UserData-Fnameuser_data.fnbbd_fnuser_data.fn, user_data.sha256_first_namefn
UserData-Lnameuser_data.lnbbd_lnuser_data.ln, user_data.sha256_last_nameln
UserData-Cityuser_data.ctbbd_ctuser_data.ct, user_data.address.cityct
UserData-Stateuser_data.stbbd_stuser_data.st, user_data.address.region/statest
UserData-Zipuser_data.zpbbd_zpuser_data.zp, user_data.address.postal_codezp
UserData-Countryuser_data.countrybbd_countryuser_data.country, user_data.address.countrycountry
UserData-AddressAll above fieldsAll above fieldsAll above fieldsAll above fields

Testing & Validation

  1. Enable GTM Preview Mode
  2. Fill out a form with user data on your site
  3. Check variable values in GTM debug panel
  4. Verify data is captured correctly from the expected source

Usage Example in Tags

// Facebook CAPI Tag
fbq('track', 'Purchase', {
  user_data: {
    em: {{UserData-Email}},
    ph: {{UserData-Phone}},
    fn: {{UserData-Fname}},
    ln: {{UserData-Lname}},
    ct: {{UserData-City}},
    st: {{UserData-State}},
    zp: {{UserData-Zip}},
    country: {{UserData-Country}},
    address: {{UserData-Address}}
  }
});
Benefits: Multi-source priority system, complete field coverage, fallback mechanisms, error handling, performance optimized, and consistent behavior across all variables.

Real-Time Dashboard

Every request that reaches your container is logged and aggregated in real time. Filter by event name, platform, country, or path. Watch your conversion ledger fill up as orders come in.

Preview Mode

Each plan includes a dedicated preview container. Connect it from GTM's Preview & Debug — you'll see every tag fire just like the production container, but isolated from your real traffic.

Troubleshooting

If events aren't reaching your destination platforms:

  1. Open your tracking subdomain in a browser — you should see the BonicBD landing page
  2. Check the dashboard's real-time view to confirm events are reaching the server
  3. Open GTM Preview & Debug to confirm tags are firing
  4. Check the platform's Event Manager (Meta) or DebugView (GA4) for incoming events
  5. If still stuck, message us on WhatsApp — we'll debug with you
Need help? Our team can do the entire setup for you, including DNS, script install, and GTM tag configuration — the technical part usually takes under an hour, and most clients are live within 24 hours. Just reach out.

Documentation FAQ

No. BonicBD is a managed setup, so you do not need to provision Google Cloud infrastructure yourself just to run server-side GTM with the platform.

Teams commonly use tracking., server., gtm., or ss. subdomains. Choose a subdomain you control and can point by CNAME to the BonicBD target.

A standard deployment is often completed within the same day once DNS is in place and the required snippets can be added to the site. More complex GTM tagging setups may take longer.

Yes. Start with the realtime dashboard and this guide, then contact support if events are still missing. DNS, route configuration, and tag logic are the most common places to check.