Skip to main content

Full Stack Implementation

This page documents the end-to-end setup for a client landing page: Cloudflare DNS, Stape, GTM (web + server), BigQuery, Google Ads events, Webflow custom code, and testing. Use it alongside the Server-Side Tracking Guide for detailed Stape/GTM/BigQuery steps.


Architecture overview

Flow: User hits the landing page (e.g. get.clientdomain.com) hosted on Webflow. The page loads the GTM custom loader from Stape (first-party subdomain). The loader runs the web container; events are sent to the server container (sGTM) on Stape, which routes to GA4, Google Ads, and BigQuery.


Phase summary

PhaseWhatOutcome
1Cloudflare DNSSubdomain for Webflow + Stape CNAMEs
2StapeServer container + custom loader domains
3GTMWeb + server containers; user-id variable
4BigQueryDataset + table; Stape integration
5Google Ads eventspage_view, cta_click, lead_form_submit
6Webflow codeGTM loader, user-id script, form hidden field, CTA tracking
7Cal.comVerify user-id in embed; optional booking event
8TestingGTM Preview, sGTM, GA4, Ads, BigQuery
10 (later)Offline conversionsn8n + Google Ads API

Phase 1: Cloudflare DNS

Once the domain is in Cloudflare (see Migrating from another CMS if the client site is on Wix), add these records. Use your actual subdomain (e.g. get for get.clientdomain.com).

DNS records

TypeNameTargetProxyPurpose
CNAMEgetproxy-ssl.webflow.comProxied (orange)Landing page (Webflow)
CNAMEdata.geteue.stape.netDNS onlyStape sGTM server
CNAMEload.data.getleue.stape.netDNS onlyStape custom loader

Alternative (root-level Stape): Use data and load.data as names if you prefer data.clientdomain.com and load.data.clientdomain.com.

SSL

  • SSL/TLS mode: Full (not Flexible).
  • Enable Always Use HTTPS for the zone.
  • HSTS optional.

Phase 2: Stape

StepAction
1Create new container in Stape (EU region). Note container identifier.
2Set server location (e.g. EU North).
3Add custom domain: data.get.clientdomain.com (or data.clientdomain.com).
4Add custom loader domain: load.data.get.clientdomain.com (or load.data.clientdomain.com).
5Enable power-ups: Custom Loader, Cookie Keeper, Click ID Restorer, Google Service Account (BigQuery), Bot Detection, GEO Headers, User Agent Info.

SSL for the custom domains is provisioned by Stape.


Phase 3: GTM containers

Web container

  • Create Web container. Note ID (GTM-XXXXXXX).
  • Add Server Google Tag (config): Measurement ID (GA4), Tracking Server URL https://data.get.clientdomain.com (or your Stape domain). Trigger: Consent Initialization (or All Pages if no CMP).
  • Add user-id variable: e.g. _c_xxx (client-specific suffix). Priority: URL param → localStorage → cookie → generate. Store in both localStorage and cookie.
  • Add Client ID set tag: writes user-id on page load. Trigger: same as config tag.

Server container

  • Create Server container. Note ID.
  • Link to Stape using the container identifier; set Tracking Server URL in Stape to your Stape domain.
  • In sGTM: route all events to GA4; route lead_form_submit (and any other conversion events) to Google Ads; send all events to BigQuery tag.

Phase 4: BigQuery

ItemValue
Datasete.g. client_events (location: EU)
Tableevents (or equivalent)
PartitionDATE(event_timestamp)
Clusterevent_name, user_id

Suggested schema (core columns)

ColumnTypeNotes
event_nameSTRINGpage_view, cta_click, lead_form_submit, etc.
event_timestampTIMESTAMP
user_idSTRINGFrom client-id variable (e.g. _c_xxx)
session_idSTRING
page_urlSTRING
page_titleSTRING
utm_source, utm_medium, utm_campaign, utm_term, utm_contentSTRING
device_type, browser, osSTRING
country, citySTRING
event_dataJSONEvent-specific payload
created_atTIMESTAMP

Enable the Google Service Account power-up in Stape and connect it to this BigQuery dataset.


Phase 5: Google Ads events

GA4

  • Create (or use existing) GA4 property. Set timezone and currency. Link Google Ads to the property.

Events to implement

EventTriggerTypical use
page_viewAll Pages (after consent)Default; can be used as secondary conversion
cta_clickClick on CTA elements (class/ID or data attribute)Primary or secondary conversion
lead_form_submitForm submit on lead formPrimary conversion for Google Ads

GTM web container

  • Page View: GA4 Event via sGTM; event name page_view; parameters e.g. page_location, page_title. Trigger: All Pages.
  • CTA Click: GA4 Event via sGTM; event name cta_click; parameters e.g. cta_text, cta_location, page_url, user_id. Trigger: Click on elements with data-cta-tracking="true" (or chosen class/ID).
  • Form Submit: GA4 Event via sGTM; event name lead_form_submit; parameters e.g. form_name, form_id, user_id. Trigger: Form submission on the lead form.

sGTM

  • Route all three events to GA4 and to the BigQuery logging tag.
  • Map lead_form_submit to a Google Ads conversion (Conversion ID + label from the Ads account). Enable Conversion Linker in both web and server containers.

Phase 6: Webflow code snippets

6.1 GTM loader (Head)

In Webflow: Site settings → Custom Code → Head Code.

<script src="https://load.data.get.clientdomain.com/gtm.js?id=GTM-XXXXXXX"></script>

Replace load.data.get.clientdomain.com with your Stape loader domain and GTM-XXXXXXX with your web container ID.

6.2 Noscript (Body)

Site settings → Custom Code → Body Code (top or bottom):

<noscript><iframe src="https://load.data.get.clientdomain.com/ns.html?id=GTM-XXXXXXX"></iframe></noscript>

Use a client-specific cookie/localStorage name (e.g. _c_xxx). Priority: URL param → localStorage → cookie → generate.

<script>
(function() {
var COOKIE_NAME = '_c_xxx';
var urlParam = new URLSearchParams(window.location.search).get('c_xxx');
var lsVal = localStorage.getItem(COOKIE_NAME);
var cookieMatch = document.cookie.match(new RegExp('(?:^|; )' + COOKIE_NAME + '=([^;]*)'));
var cookieVal = cookieMatch ? cookieMatch[1] : null;
var cid = urlParam || lsVal || cookieVal || (function() {
var id = 'cid_' + Date.now() + '_' + Math.random().toString(36).slice(2, 11);
try { localStorage.setItem(COOKIE_NAME, id); } catch (e) {}
document.cookie = COOKIE_NAME + '=' + id + ';path=/;max-age=31536000;SameSite=Lax';
return id;
})();
if (!lsVal && cid) try { localStorage.setItem(COOKIE_NAME, cid); } catch (e) {}
if (!cookieMatch && cid) document.cookie = COOKIE_NAME + '=' + cid + ';path=/;max-age=31536000;SameSite=Lax';
})();
</script>

Replace _c_xxx and c_xxx with your client’s ID name.

6.4 Form hidden field

  • Add a Hidden field to the Webflow form. Name/ID: e.g. hidden-field1.
  • Populate it on load (after user-id script). Example, using same COOKIE_NAME as above:
<script>
window.addEventListener('load', function() {
setTimeout(function() {
var COOKIE_NAME = '_c_xxx';
var lsVal = localStorage.getItem(COOKIE_NAME);
var cookieMatch = document.cookie.match(new RegExp('(?:^|; )' + COOKIE_NAME + '=([^;]*)'));
var cid = lsVal || (cookieMatch ? cookieMatch[1] : null) || 'nocookie';
var field = document.querySelector('#hidden-field1') || document.querySelector('input[name="hidden-field1"]');
if (field) field.value = cid;
}, 1000);
});
</script>

6.5 CTA click tracking

  • On each CTA link/button, add attributes: data-cta-tracking="true", data-cta-text="Button label", data-cta-location="hero" (or similar).
  • Either use GTM’s built-in Click trigger (Click – All Elements, filter by data-cta-tracking = true) and pass click element text/attributes into the tag, or push a dataLayer event on click and trigger the GA4 event from that.

Example dataLayer push (if you prefer to fire from custom JS):

document.querySelectorAll('[data-cta-tracking="true"]').forEach(function(el) {
el.addEventListener('click', function() {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'cta_click',
cta_text: el.getAttribute('data-cta-text') || el.textContent.trim(),
cta_location: el.getAttribute('data-cta-location') || '',
page_url: window.location.href
});
});
});

Phase 7: Cal.com (and similar embeds)

  • User-id: Pass the same client-id (e.g. _c_xxx) into the Cal.com embed via its prefill/hidden field (see Third-party embeds and tracking).
  • Booking conversion: Optionally fire a cal_booking_complete (or similar) event via Cal.com webhook to sGTM or via PostMessage to the parent page and then dataLayer.push; include user_id for attribution.

Phase 8: Testing

CheckWhere
GTM Preview: page_view, cta_click, lead_form_submit fireGTM Preview mode
User-id present in events and in form hidden fieldGTM variables / form submit payload
Events reach sGTMStape dashboard / sGTM debug
GA4 Real-Time shows eventsGA4 → Reports → Real-time
Google Ads conversion (lead_form_submit)Ads account; may lag 24–48 h
Rows in BigQueryQuery events table
Cross-browser (Chrome, Safari, Firefox)Manual test
Form submit includes user-idBackend or form tool logs

See Server-Side Tracking – Testing & Validation for more detail.


Phase 10 (later): Offline conversions

  • Design workflow (e.g. CRM or sheet → n8n → Google Ads Upload API).
  • Use the same user-id / click-id where possible for matching.
  • See Google Ads (tool) for existing offline conversion patterns.

TopicDoc
GTM, user-id, form patternGTM (tool)
Stape setupStape (tool)
DNS, SSLCloudflare (tool)
BigQueryBigQuery (tool), BigQuery integration
Google Ads conversionsGoogle Ads (tool)
End-to-end sGTMServer-Side Tracking Implementation