Category Archives: Google Tag Manager

Logging Oversized Enhanced ecommerce Google Analytics hits in Google Sheets


If you ever worked on a Enhanced Ecommerce implementation project there is a chance you encountered the ‘Payload size is too large (15226).  Max allowed is 8192.’ message which actually means there will be nothing stored in reports. There are some workarounds for this particular issue such as:

In this post I’ll only focus on explaining how to determine the EE actions and hits the payload may be considered an Issue.

Step 1 – Google Sheets setup

The Sheets procedure and the entire script which reports on the questionable hits can be found here – (by Martin Hawksey). Follow the entire procedure and do not forget to publish and update the Script once you made the necessary changes!

A quick note on the Google Sheet itself. This one needs to be filled with header row so that we can map our payload to Google Sheets as in this example:


Just the header (1st) Row. The column entries are case sensitive so whatever you define in the payload sent to Google Sheets needs to exact match! For my example I used these column labels (you may notice a ‘slight’ resemblance to Measurement protocol parameters):

  • timestamp
  • payLoadLength
  • tid (tracking id)
  • cid (client id)
  • uid (user id)
  • t (type of hit)
  • pa (product action)
  • ni (non interaction)
  • dl (document location)
  • dp (document path)
  • dt (document title)
  • ec (event category)
  • ea (event action)
  • el (event label)
  • ti (transaction id)
  • tr (transaction revenue)

Make sure that the cid and ti columns are formatted as Plain Text otherwise some unwanted auto formatting may occur. Additional parameters may be added as needed.


Step 2 – Google Tag Manager Setup (Mimic the Google Analytics payload)

When proceeding with this it would be prudent to create an additional GA tag in GTM leading to a dummy GA property just to be sure nothing goes wrong with the data. Basically add a new field to set to each GA tag which passes on EE payload as in the following example:


So I will use the sendHitTask as means to read the entire payload and send it to Google Sheets once the payload exceeds a certain length. The sendHitTask field will use a Custom JS variable (Code below – based on this post) which returns a function which is designed (apologies for the sucky code) to push the default payload to GA and further on send the custom payload to Google Sheets – :

function sendHitTask(){
  return function(model) {
    var payLoad = model.get('hitPayload');                     
    var trackingBaseUrls = ['', ''];

    for (i = 0; i < trackingBaseUrls.length; i++) { var baseUrl = trackingBaseUrls[i]; if (trackingBaseUrls[i].indexOf('collect') > -1) {
          var req = new XMLHttpRequest();
'POST', baseUrl, true);
      } else if (payLoad.length > 7000){
        var payLoadExtract = payLoad.split('&');
        var payLoadArray = {};
        // Push values to array for later access
          for (i = 0; i < payLoadExtract.length; i++){
                  var splitArray = payLoadExtract[i].split('=');
                  payLoadArray[splitArray[0].trim()] = splitArray[1].trim();
            // Specify values to be sent to Google Sheets from array
            var tid = 'tid=' + payLoadArray.tid,
            cid = '&cid=' + payLoadArray.cid,
            uid = '&uid=' + payLoadArray.uid,
            t = '&t=' + payLoadArray.t,  
            pa = '&pa=' +,
            ni = '&ni=' +,
            dl = '&dl=' + payLoadArray.dl,
            dp = '&dp=' + payLoadArray.dp,
            dt = '&dt=' + payLoadArray.dt,                                      
            ec = '&ec=' +,                                      
            ea = '&ea=' + payLoadArray.ea,                                      
            el = '&el=' + payLoadArray.el,                                      
            ti = '&ti=' + payLoadArray.ti,                                      
            tr = '&tr=' +,                                      
            timestamp = '&timestamp=' + {{UTIL - Date}},
            payLoadLength = '&payLoadLength=' + payLoad.length;
            var collectPayLoad = tid + cid + uid + t + pa + ni + dl + dp + dt + ec + ea + el + ti + tr + timestamp + payLoadLength;
        // Send Values to Google Sheets
        var collectUrl = baseUrl +'?'+ collectPayLoad;
        var myImage = new Image();
        myImage.src = collectUrl;

And this is basically it. Whenever this tag is triggered it will push data to GA but if the payload exceeds the defined condition it will log it in the Google Sheet which will hopefully help you in debugging the implementation.

Site Speed reports in Google Analytics

A small refresher course in how you deal with site speed reports and page timings in particular. As Google announced site speed to be one of the ranking factors the issue got more ‘real’ – a wast number of articles show the importance of site speed and its effect on the business outcome (a recent one by Stéphane Hamel shows some numbers – link) yet the most important thing is the end user which may or may not like slow loading pages.

This article will present 2 simple things:

  1. Technical manipulation of the site speed measurement
  2. Averages suck – drill down

How to set the site speed feature for Google Analytics (Page timings)

Some basic facts:

  1. Site speed info is sent without any additional GA code needed
  2. Site speed info is sent with pageview hits
  3. Site speed sample can be increased reduced with additional code
  4. Default site speed sample size is described here

The implementation part if you want to manipulate the sample size is quite easy as you only need to slightly adapt the basic GA tracking code (info on the siteSpeedSampleRate field):

ga('create', 'UA-XXXX-Y', 'auto', {'siteSpeedSampleRate': 50});

Note that the values expected are 0-100. The default value, without any code adaptation, is 1%.

With GTM it is even easier to do this. It also allows you to be flexible if you want to determine for which part(s) of the site you actually want to see a larger sample – more important parts of the site.

For instance if you by any chance use dynamic remarketing there already is a variable present which you can freely use such as ecomm_pagetype which provides info on the page type viewed (conversionintent, searchresults, category, product etc.).

A simple example of how to set siteSpeedSampleRate in GTM

This example is based on the assumption you already have dynamic remarketing variable present on the site (if not you can always create a customJS variable and set the siteSpeedSampleRate based on Page URL or similar).

Variable 1 – fetch the ecom_pagetype value (example based on dataLayer)

Screen Shot 2016-02-12 at 09.29.34

Variable 2 – set the siteSpeedSampleRate based on value from ecomm_pagetype variable

Screen Shot 2016-02-12 at 09.31.07

Just to explain a bit we set the input values as expected strings from the ecomm_pagetype variable and set the output as values used for the siteSpeedSampleRate field – a lookup table variable is used in this case. And one additional thing if the variable does match any of the explicitly defined strings we set the default value to 1 (you can freely change this to any number you prefer (0-100)). By doing this you effectively increase the chance the timing hit will indeed be sent.

The Google Analytics Pageview Tag – one and only in this case 

Screen Shot 2016-02-12 at 09.32.30

The only thing you need to set inside your main Pageview tag is the Fields to set part – Field Name: siteSpeedSampleRate and Value: {{util – siteSpeedSampleRate}} (or any other variable name you set). Publish.

The results

Nicely described here –

The averages suck part

With page timings be very very careful when analysing data as for some pages the sample can be so small it can easily lead you to wrong conclusions.

A small example on data collected from this blog. When looking at Behavior > Site Speed > Page Timings report there will probably be pages which are above or below average for a metric such as avg. page load time.

Screen Shot 2016-02-12 at 09.49.28

Issue detected? Of course you would need to check if that page has an obvious reason as it can be resource heavy so try not to compare to average but with similar pages (page types – use Content Grouping!). A very simple approach to continue detecting the reason if nothing obvious is found – secondary dimension. Just click on the page in question inside the table report and add a secondary dimension which will reveal the context – as an initial idea try Country, Device Category, Browser or similar. For example:

Screen Shot 2016-02-12 at 10.00.03

Yes, our site on mobile sucks. But do not be hasty in making such decisions – always use an additional segmentation to prove this hypothesis. So lets see the issue with an another Secondary dimension:

Screen Shot 2016-02-12 at 10.05.49

Now you can definitely see  there are some outliers (in a sucky small sample of data) which affect the average heavily which can defeat any initial hypothesis we created.

Standard reports will only get you so far. For more in depth look at the issue create a Custom report which will allow for a more granular look at the data and some more metrics (more appropriate). Create something like this – custom report link where it is much easier to find the ‘real’ culprit and act on it.




Sending a Custom Metric with a Pageview Tag with GTM

One would think it is easy to send a Custom Metric with a pageview yet apparently there are some mishaps which may occur due to something obscure as a timing hit. Reading through the recent article written by Yeshoua Coren describing custom and calculated metrics there was a mention of this being the issue and the idea on how to solve it though not in detail (hitCallback) – fortunately enough I have only sent Custom metrics with events which by default do not add an additional timing hit so this article is just curiosity based:).

The issue

The real issue is when you send a pageview hit which has the set custom metric if there is a subsequent timing hit the custom metric will be inflated or better say sent twice. The volume of this additional hits is very well described on Google official developer site.  So if by any chance you opt to set Fields to set field siteSpeedSampleRate like this – your reports may be polluted:


So when you send a pageview this will happen (from debug):


GTM uses set on a named tracker – tracker name is in this case gtm1453223458541 so for each subsequent hit (while it persists) for this tracker name the Custom metric will be sent again. Fortunately a subsequent event will not be affected as the name of the tracker will changed as it uses timestamp in naming convention. And now the timing hit which follows immediately after the pageview hit:


The solution (so far)

In your Pageview tag add Fields to set Field > hitCallback:


The value will be a Custom  JavaScript variable (name it as you see fit) (note that with named trackers – when you explicitly define the tracker name the situation is ‘a bit different’):

function() { 
   return function() { 
      ga(function() { 
         tracker = ga.getAll()[0].get('name');
         ga(tracker+'.set','metric2', 0);

Once you set this up the debug will show:

Running command: ga(“gtm1453224328813.set”, “metric2”, 0) which will set the value to 0 for the subsequent timing hit.


Please note that this applies to simple implementations only and should be revised especially in cases where:

  • more trackers are present
  • you use named trackers

Would be extremely happy to hear if there are any other workarounds for the issue!:)

How to track internal campaigns using Google Analytics

Let’s start with the basics …

What is an internal campaign

An internal campaign, from a business perspective, is any element on a site(s) which links to your content and its purpose is to speed up the purchase decision, highlight a product or category, give incentive in any part of the purchase funnel and more. A simple example would be a click on banner which is placed on our home page – – which links to a product page (also on our site(s)) –

Some examples / methods of internal campaigns:

  1. urgent – offer ends in 2 hours
  2. limited – only 5 left
  3. popular – top 5 products
  4. deal – buy 1 get one for free

The truth of the matter is internal campaigns are often used to push products with these common attributes – hard to sell, large stock, high margin and let’s not forget fill the gaps in design:)

A simple example ( – part of the homepage):


Why track internal campaigns

The obvious reason is – why not. Internal promotions often take a lot of screen real estate and this fact should not be taken lightly. Aside from that you need to invest time and money for campaign production – copy, images, … so in short you owe it to yourself to prove / test its (in)efficiency. From a marketeers perspective there are some quite useful goodies gained from tracking internal campaigns – what works and what does not:

  1. copy
  2. incentive type
  3. position
  4. colour
  5. season
  6. category
  7. price
  8. and so much more


Before we get to how to track these campaigns …

How NOT to track internal campaigns

Do not use UTM tagging to track internal campaigns. Never. Never ever.

There may be some cases where you feel tempted to use UTM taging such as a multisite environment where you own multiple properties – still, don’t.

A common error in web analytics is using features like UTM tagging for the wrong reasons resulting in bad data. If we opt to tag internal campaigns with UTM we actively affect Google Analytics traffic source attribution. Let’s see what happens if we do:

  1. A user clicks on our Google AdWords ad – Google Analytics will report on this session as coming from google / cpc
  2. Inside that session the same user clicks on our banner which has UTM tags (utm_source, utm_medium, …)
  3. This results in traffic being attributed to our banner and not Google Adwords (and will create a new session)
  4. If a user activates a type of conversion on our site standard report will attribute it to Internal campaign while the MCF part of reports will attribute Google AdWords as an assisting channel in the conversion path – a mess not easy to decipher

How TO track internal campaigns

You will probably encounter a few main methods of how to properly track this sales tool. The most popular ones are:

  1. Site search – this was nicely explained by none other than Justin Cutroni
  2. Enhanced ecommerce – this is probably the first Google Analytics feature which has a specific area designated for internal campaign (promotion) tracking this is also by far the most difficult method of tracking (as tracking internal promotions makes real sense only if you track all other EE activities on the site)
  3. Event tracking – a very flexible method of tracking almost anything on your site (I will describe some of the basic ways to do event tracking with Google Tag Manager)
  4. Custom dimensions – in addition to event tracking we can add a new layer of data to existing hits (events / pageviews) in order to better describe the actual internal promotion

As the methods 1 and 2 are described thoroughly let’s focus on event tracking and custom dimensions and we will do a hybrid of these 2.

Tracking Internal campaigns with Google Analytics Event tracking

First of all we need to decide the naming policy for our event / campaign tracking efforts. In short what will be the category, action and label values used throughout the site – note that if the naming is not consistent, time and site wise, it will be really hard to do meaningful reports. So lets revert to the basic questions – what would we like to know:

  1. Does the internal campaign help sell stuff? – we have that covered – create a segment using category / action / label and track conversion behaviour
  2. Which internal promotions are most likely to sell (and why – tricky but at least try)? – we need info here on:
    1. promotion placement (page, position)
    2. size, color, copy (think in terms of utm_content)
    3. incentive type (price, urgency, loyalty, package, any additional added value)
    4. some other attribute

As you can imagine it is not a small feat to push all these values into 3 event hit default dimensions so there is always the option to add a custom dimensions in the mix to help out (if you can spare the Custom dimensions slots!!! – note there is a limit of 20 CDs per property). An initial suggestion would be:

  • Event category = ‘internal_campaign’ (if we have a rollup property we can add the site the click happened on so it would be ‘internal_campaign_sitename’ (think utm_source))
  • Event action = ‘shoes_men_spring_2015’ (think utm_campaign)
  • Event label = ‘250*250|red|buy_now|a|2’ Ad / Banner / Campaign content (think utm_content) – size of the banner, color, CTA, variation, position (if a slider present)
  • CD1 = incentive type or some other attribute = hit scope dimension

Now that we solved what we wanted to report on let’s see how can we retrieve these values. this part will emphasize on the fact that GTM is not a one click install system and it often requires additional development work on site as the site itself is rarely built to provide info which marketeers need in an easy to consume / report form.

Trigger the Google Analytics Tag Event tag

So lets first solve the triggering part where we want to send a GA event only when someone clicks on an Internal campaign ad / link. For this case we need a way to identify the click on the internal campaign.

Here are a few basic options to solve the trigger part:

  1. use the DOM event – onmousedown, onclick – <a href=”#” onclick=”dataLayer.push({‘event’ : ‘trackEvent’}];”> – use the onclick event to push a custom event via dataLayer
  2. use GTM click / linkClick triggers which track clicks on DOM elements / links – these are GTM built in events – <a href=”#” id=”intcmp” data-gtm-data=”someImportantInfo”> – use of GTMs click / linkClick trigger in combination with auto event variables (element attributes (Click ID or element attribute auto event variable – data-gtm-data))

Trigger case 1 (DOM event)


Trigger case 2 (GTM click / linkClick)


Enable when part is optional and takes care of performance issues so you do not listen the clicks on lets say all pages but only ones that contain internal promotions. When using Auto event variables make sure the ones you will use are activated in the Variables section of GTM interface:



How to set and read the data payload for GA event tag

And now we come to the tricky part. It is not hard to send basic info to Google Analytics yet it is quite hard to come by ‘ready to consume info’ from the site. Let’s look at an example – this is not a good example of how to do it properly but an example of what you can usually expect:

<div class="slide" style="width: 980px; float: left;">
<a href="/running.html">
<img src="" alt="">

As you can see here there is almost no notion of the stuff we would really like to track – position, size, CTA, copy, color and similar. In addition to that there are no clear identifiers of the element being the internal campaign link aside from the class=”slide”.  We have two main options here:

  1. Scrape whatever we can from DOM – Click URL, image name (this gives us a really limited data set in most cases)
  2. Add some code which would better describe what the user has actually clicked (better yet we usually need developer support – preferred version)

Option 1
A very simplified way of sending the info to GTM and subsequently to Google Analytics would be to use a dataLayer custom event:

<a href="#" class="someclass" id="internalcamp" onmousedown="dataLayer.push({'event' : 'trackEvent','eventCategory': 'internal_campaign','eventAction': 'shoes_men_spring_2015','eventLabel': '250*250|red|buy_now|a|2','customDimension1':'price'});">

Option 2
An option where you have lots and lots of Custom dimensions slots available:

<a href="#" class="someclass" id="internalcamp" onmousedown="dataLayer.push({'event' : 'trackEvent','eventCategory': 'internal_campaign','eventAction':'shoes_men_spring_2015','customDimension1':'250*250','customDimension2':'red','customDimension3':'buy_now','customDimension4':'a','customDimension5':'2','customDimension6':'price'});">

Option 3
Yet another option for streamlining event information to GTM is to use HTML data attributes – I personally find this approach very useful but it is usually applicable with long term relationship clients where analytics needs are both a part of the development and marketing process (additional benefit – whatever changes in terms of class or id of the element the data-gtm- logic usually stays intact). So for example:

<a href="#" class="someclass" id="internalcamp" data-gtm-event="internal_campaign" data-gtm-action="shoes_men_spring_2015" data-gtm-label="250*250|red|buy_now|a|2">

The only thing you need to do now is to ‘catch’ the data on link click in GTM and here is how you do it:

Values needed to identify the right event and to push the final payload to event.

3 variables are needed all type of Auto Event variables where you choose Element Attribute and create all 3 data-gtm- variations.


How we trigger the Google Analytics event from GTM.


Where we send the entire payload to Google Analytics with an event.


Alternative pageview tracking with custom dimensions

Let’s assume you cannot rely on data attributes and events and your only option is adding parameters to links – it is doable and here is how to do it.

Your internal campaign directs to a page on your site: (old URL) which you should change to …*250

Follow up steps …

Read Url fragment – URL type variable where we read everything after #.


Custom dimension value extract from URL fragment (you can extract the values to one dimensions or parse the values to separate dimensions) – note that we split the entire string by ‘&’ and use the index 0 which is the 1st item and will in our case return the string ‘intcmp’:

function() {
var myTempString = {{read URL fragment}};
if(myTempString && myTempString.indexOf('intcmp')>-1){
return myTempString.split('&')[0];

An All pages for the pageview tag will suffice. If the fragment values do not exist nothing will be passed as custom dimensions values – will not be processed.

Use a Google Analytics Universal template tag with the following settings:


And this is it – happy tracking!

Time to complete a goal or transaction using GTM and Google Analytics

Here is a quick overview of how to track the time needed for a user to finalize a desired activity inside a session using Google Tag Manager and Google Analytics. The example in this article is a simplified overview of various hits and methods on how we can achieve process tracking inside Google Analytics. By default Analytics measures Session length / duration and time on page which is quite challenging in this cases and rarely applicable thus one may be forced to use features like custom metrics and / or user timing hits.

Case – time from session start to lead generation form submit

A user starts a session and is expected to fill and submit a lead generation form. We would really like to know how long this process usually takes (not the entire multichannel / multi device experience in this case but just the in session experience).  The experience will be highly affected by previous experience with the site – so using some segments while analysing would come in quite handy.

The idea on how to fetch these values is quite simple. Drop a cookie once a session starts, or in some specific cases when an on site process starts (e.g. checkout) – where the cookie value will be the session / process start time. After the user has completed the process – successfully completed the desired action (we basically mimic Analytics goal behaviour – so it is either a destination or an event based trigger in GTM) we send a User timing hit or an event hit with a time type custom metric.


The procedure

What is needed:

1. Cookie tag – Custom HTML
On session start we set a session level cookie.

var d = new Date().getTime();
document.cookie = "sessionStart=" + d + "; path=/;";

Trigger – Pageview using a 1st party cookie variable sessionStart (so fire only if sessionStart is undefined).
Variable – 1st party cookie – sessionsStart ({{gtm sessionStart}}).

trigger first page in a session

 2. Google Analytics pageview tag – Template tag – Universal Analytics

This is a basic GA universal pageview tag yet I added a hitCallback inside ‘Fields to set’ to safely set a clientId as a custom dimension (user level – do not forget to dreate the custom dimension inside GA admin) in an additional event tag (ty Simo!) – this will fire a Google Analytics event tag only when the Pageview tag has fired successfully. so the setup looks like this:

universal analytics main pageview tag

Trigger – All pages (default trigger).
Variable – custom js variable which send a custom event via dataLayer push – this event is used for the Utility Event responsible for adding a custom dimension with clientId value.

check if ua pageview fired

function() {
return function(){dataLayer.push({'event': 'pageViewFired'});}

3. Google Analytics Event tag – Template tag – Universal Analytics

A simple event tag which only purpose is to set the Custom Dimension value of the users clientId (this is used to further analyse time for each process based on the client Id – some randomization could be utilized if a user is expected to have multiple conversions or even add a process end time as a custom dimension or use time dimensions in GA).

event tag set cid

Trigger – event equals pageViewFired.
Variable – use a custom JS variable which will fetch the clientId value.

function() {
return ga.getAll()[0].get('clientId');

4. Google Analytics User timing tag – Template tag – Universal Analytics

This is where we send info to GA that the process has successfully ended and pass the time value. So the setup looks like this:

user timing hit

Trigger – any which identifies the session has resulted in a goal completion. For this case I used a simple custom event – trackConversion – dataLayer.push({‘event’:’trackConversion’});

Calculation and variables – as you noticed a different variable is used for the custom metric and user timing value. The reason for this is that user timing hit requires a value in milliseconds and custom metrics in seconds. The calculation is pretty simple.  You need 2 variables:

User timing value

function (){
var endTime = new Date().getTime(); // this could easily be a separate variable
return endTime - {{gtm sessionStart}};

and Custom metric value

function (){
var endTime = new Date().getTime(); // this could easily be a separate variable
return (endTime - {{gtm sessionStart}})/1000;

And that’s basically it – one word of caution though user timing hits are prone to sampling so consider sending the timing info through events with CD and CM defined.

When you are done setting this up the example report could be something like this:

report example


Hope the article was useful and of course happy tracking!


iFrame Google Tag Manager Cross domain tracking alternative

Hi to all who have dealt with the cross domain issue when working with iFrames – it can be a tedious endeavour yet using Google Tag Manager and Universal Analytics the process is somewhat simplified. This is also an updated version of a similar method yet this one uses the Google Analytics Template tag inside GTM.

There already are some great articles which offer a complete set up workflow such as a great Article by Claudia Kosny found on – – yet we found it to be a bit too complex when working without a decent developer support and the main issues were:

  • iFrame will not show if Google servers do not respond and
  • iFrame will not show if the GTM setup was misconfigured

If the iFrame was on the site to render some critical information such as Booking engine or any kind of Payment service it was just too big of a risk.
Fortunately Google updated the iFrame section of cross domain tracking with a new method which does not have this kind of problem – Tracking Cross Domain iFrames using postMessage –

Downside to this method is:

  • users with IE7 and earlier will be tracked as different clientIDs (cross domain tracking will not be in effect yet tracking will still occur)

To be clear the end goal of this implementation is to preserve the session and have accurate attribution reporting inside Google Analytics – this of course includes the ability to set up funnels and any additional hits within the existing session (without messing up traffic sources and session inflation when using Universal Analytics).

How to set up iFrame cross domain tracking for Universal Analytics via GTM

1. Referral exclusion

The first step is to always prepare the referral exclusion list. Our example will use the domains (parent domain) and (child domain – page on domain inside iFrame).

2. GTM container

Place the GTM container snippet on both domains! More info can be found in Google Help center

3. GTM settings (GTM Tags, triggers, variables and more)


Triggers help you define when a tag should be activated using various conditions.

1. all pages parent (parent domain)
A tag using this trigger will activate only on pages with hostname (our parent page). You can opt to change the trigger type to either DOM or Page View just make sure you test the setup.
{{Page Hostname}} > match case (can be any which defines the situation) >

all pages parent trigger

2. all pages child (child domain – inside iFrame)
A tag using this trigger will activate only on pages with hostname (our iFrame page).
{{Page Hostname}} > match case (can be any which defines the situation) >

all pages child trigger

3. event on child trackPage (fires the page on child domain after it has dealt with the cid retrieval)
A tag using this trigger will activate only on pages with hostname (optional) and only when the custom event trackPage has been pushed to dataLayer.
Event name: trackPage
{{Page Hostname}} > match case (can be any which defines the situation) > (optional)

event child domain trigger


The following examples are basically utility Variables with which you can easily reuse the entire setup in just a few edits – in short you do not need to mess with the code just edit everything with variables.

1. dl cid (Data Layer)
A Data Layer type variable which returns value of cid once it is pushed to dataLayer.

2. dl virtualPagePath and dl virtualPageTitle (Data Layer)
Just an example of what you can additionally define for tags e.g. booking steps or similar.

3. gtm allowedOrigins (Constant)
Used to set the whitelisted sites (i.e. your booking engine, payment system, CRM) which can communicate to our parent Domain.
Value:, (a list of comma separated domains where our page inside iFrame is placed)

4. gtm topOrigin (Constant)
Used to define the domain which holds our parent Page – parent domain.
Value: (our main domain)

5. gtm uaTrackingId (Constant)
We will store the UA tracking ID (property ID) in a Macro so we can reuse it all of our GA tags with less chance of human error:) (you can of course change it to lookup, dl or custom JS)


We will need 3 separate tags – 2 Custom HTML and one plain Universal Pageview tag for the complete setup to work. Custom HTML Tags are used to retrieve the cid parameter (code is provided in the full container download). Do not forget to add the clientId under fields to set!

Custom HTML #1 (on child domain only (inside iFrame)):

// Add the expected origin domain inside gtm topOrigin variable
// Must use the following format
var topOrigin = {{gtm topOrigin}}; 
function xDomainHandler(event) {
  event = event || window.event;
  var origin = event.origin;
  if (topOrigin != '*' && topOrigin != event.origin) {
  try {
    var data = JSON.parse(;
  } catch (e) {
    // SyntaxError or JSON is undefined.
  if (data.cid) {

if (window.addEventListener) {
  window.addEventListener('message', xDomainHandler, false);
} else if (window.attachEvent) {
  window.attachEvent('onmessage', xDomainHandler);

var alreadySent = false;
function sendHit(cid) {
  if (alreadySent) return;
  alreadySent = true;
  // If cid exists, it will overwrite any existing values
  var params = {};
  if (cid) params['clientId'] = cid;
  dataLayer.push({'event':'trackPage','cid': cid});
if (!window.postMessage) {
  // If no postMessage Support.
} else {
  // Tell top that we are ready.
  top.postMessage('send_client_id', topOrigin);
  // Set a timeout in case top doesn't respond.
  setTimeout(sendHit, 100);


Custom HTML #2 (on parent domain only ):

// Edit the gtm allowedOrigins variable and add your domains to the whitelist
var allowedOrigins = {{gtm allowedOrigins}}.split(',');
function xDomainHandler(event) {
  event = event || window.event;
  var origin = event.origin;
// Check for the whitelist.
  var found = false;
  for (var i = 0; i < allowedOrigins.length; i++) {
    if (allowedOrigins[i] == origin) {

      found = true;
  if (!found) return;
  if ( != 'send_client_id') return;

// Get the clientId and send the message.
  var tracker = ga.getAll()[0];
    var data = {cid: tracker.get('clientId')};
    event.source.postMessage(JSON.stringify(data), origin);
if (window.addEventListener) {
  window.addEventListener('message', xDomainHandler, false);
} else if (window.attachEvent) {
  window.attachEvent('onmessage', xDomainHandler);


The final tag setup will look like this:

cross domain tags




ua pv main tag



You can download the full container version here – please be careful when you import the  container you do not: overwrite your existing container or even import into a production container rather test it first in a new account / container and migrate settings after a successful testing process.

4. Publish

After you finish all of the setup please do not forget to debug, preview and create a version and publish in the end.
Happy (cross domain) tracking!