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)
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.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:
- Head code (Google Tag Manager loader) — paste at the very top of
<head>on every page. - Body noscript — paste right after the opening
<body>tag. - 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.
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
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.
Consent Mode V2
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:
- BBD Context -
window.BBD_CONTEXT.user_data - dataLayer - Enricher events and
user_dataobjects - Cookies -
bbd_pii,_bbd_ppi,bbd_id_cache
Installation Steps
- Go to GTM → Variables → New
- Set Variable Type to Custom JavaScript
- Copy the function code for each variable from below
- Save with the exact variable name (e.g.,
UserData-Email) - 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-Email | user_data.em | bbd_em | user_data.em, user_data.sha256_email_address | em |
| UserData-Phone | user_data.ph | bbd_ph | user_data.ph, user_data.sha256_phone_number | ph |
| UserData-Fname | user_data.fn | bbd_fn | user_data.fn, user_data.sha256_first_name | fn |
| UserData-Lname | user_data.ln | bbd_ln | user_data.ln, user_data.sha256_last_name | ln |
| UserData-City | user_data.ct | bbd_ct | user_data.ct, user_data.address.city | ct |
| UserData-State | user_data.st | bbd_st | user_data.st, user_data.address.region/state | st |
| UserData-Zip | user_data.zp | bbd_zp | user_data.zp, user_data.address.postal_code | zp |
| UserData-Country | user_data.country | bbd_country | user_data.country, user_data.address.country | country |
| UserData-Address | All above fields | All above fields | All above fields | All above fields |
Testing & Validation
- Enable GTM Preview Mode
- Fill out a form with user data on your site
- Check variable values in GTM debug panel
- 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}}
}
});
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:
- Open your tracking subdomain in a browser — you should see the BonicBD landing page
- Check the dashboard's real-time view to confirm events are reaching the server
- Open GTM Preview & Debug to confirm tags are firing
- Check the platform's Event Manager (Meta) or DebugView (GA4) for incoming events
- If still stuck, message us on WhatsApp — we'll debug with you
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.