Implementing cross-domain iframe tracking is a pain that many analytics and digital media professionals go through at least once in their careers.

The truth is, it’s ugly and should be avoided at all costs. But sometimes, for things like third party booking platforms, website owners can integrate the service to get a popup booking application on their website with one line of code. This is pretty commonplace for restaurants and hotel booking widgets.

There’s a few reasons this isn’t always tackled head-on. The first is that typically these platforms didn’t include ways to inject tracking codes as the platforms were mostly locked down. And the second is that the JavaScript code required to implement looks scary for internet marketers who can’t usually code. The browser API needed for this (postMessage) to work has actually been around for way over a decade, so there’s no excuse!

Note: For this to work, you will need access to either edit the iframe source, be able to add in a Google Tag Manager container, or at the very least to provide JavaScript snippets for goals and events.

Can’t we just use Google Analytics?

If you do have access to trigger a JavaScript event on a successful form completion within a iframe popup widget, you should resist the temptation to directly insert Google Analytics (or another web analytics platform) as this data will actually be captured in a different session, and therefore have no context to the source (or medium, campaign, term, etc) which created this traffic in the first place.

When an iframe form is embedded into a page, we’re actually interested in firing any tracking events to the ad and analytics networks via the parent page, as this has all of the contextual information we need in order to fire events and goals which attribute to your marketing efforts.

How does this work?

When an iframe is embedded into a web page, it is effectively a locked down window into another separate URL — usually on a different domain. Scripts from the parent page cannot impact what happens inside the iframe for security reasons, and in Google Analytics, embedded pages typically show as direct traffic because the Client ID is not passed between the pages/sessions. Note, that you could start investigating the Google Analytics Linker plugin for iframes to pass the Client ID from parent to child, but it’s almost not worth the effort when compared with this solution! 

However, thankfully, the child iframe can communicate upwards to any parent frames/pages, which we can leverage to send notifications when events are completed, such as form fills, button clicks and purchases. If the child iframe page let’s the parent page (with GA running) know, then we can pass those event details straight on to your advertising platforms to register a goal completion for the recorded source/medium.

Three are two steps required to making this solution work in Tag Manager;

  • adding a listener to the page on your website so that if an event fires, you can deal with it, and
  • firing the event from the iframe in a specific format so that it filters up to the parent page to interact with your listener.

From these steps, you then have the ability to send Data Layer events to directly or indirectly trigger events in your advertising and analytics platforms.

Implementing the solution with Google Tag Manager

1. Either in a separate Tag Manager account, or by asking your iframe developers to implement code directly, insert the following code to be fired upon a successful trigger — for example, making a booking. Customise your domain name and if you want, the event payload too. As you can see in my example, we’re passing two variables; ‘event’ and ‘form’. If you or your iframe content developer want to pass messages to any domain in which it is embedded (be careful about sensitive content!) then just use an “*” asterisk symbol in quotes, rather than domain.

2. Next, set up your listener on all valid pages on your domain that you expect to receive the events from. I set this up through Tag Manager, as we roll this out to all client sites to enable tracking like this, however you can add this code to your website’s code if you would like to. I set this to trigger on every page, but if you know an iframe will only ever be present on a subset of pages, then for efficiency, it’s best to set up an appropriate trigger in Tag Manager.

3. Now, as you may be able to read, on lines 9-12 above, there is a dataLayer.push() event defined, where the event name is taken from the original iframe window’s event. This event name is now accessible inside Tag Manager as a custom event, so that you can set up your GTM tagging as you see fit. There is additional information we fire here into the Data Layer under postMessageData, but for the sake of simplicity, this is beyond the scope of this tutorial. Use this to differentiate different form names which you send between the frames as set in my example.

4. Configure a custom GTM trigger of type “custom event” which listens for your original event name. Mine was: “iframeFormSubmit”.

5. Next, set up your tag in Tag Manager and assign this new custom trigger as the tag’s trigger.


At this time, it’s probably a good idea to start testing your implementation. Put your Tag Manager account into preview mode and begin testing, ensuring you also have your Chrome Tag Assistant plugin recording Google Tag Manager and Google Analytics events, too.

Let me know in the comments if you have any issues or suggestions for improvements.

Aaron Dicks

Performance Director

Performance Director of Impression. Search engine optimisation, paid media and web analytics consultant. Also programmer and digital all-rounder. @aarondicks

Aaron has specialist knowledge in SEO, PPC and Analytics Consultancy.

5 thoughts on “Cross domain iframe tracking via Google Tag Manager

  1. Amrdeep Athwal says:

    Hi aaron, much thanks for this article had the same issue with a client and was ablle to get tracking set up with your help.
    I wanted to ask how I would go about setting up multiple events within an iFrame do you need to use separate posts or can it be done in one large post.

  2. DP says:

    This is a fantastic article! Solved my problem on the first try.

    1. Aaron Dicks says:

      Glad we could help!

  3. Dayse Pessoa de Azevedo says:

    Hey Aaron!! Very nice post, but for me it didn’t work out =( I am kindda of lost with this event. I should create the tag of the event in the parent site or in the child site? And the first html that you shared I have to put the domain of the parent site, right? I’ll wait for your answer. Thank you in advance!!

    1. Aaron Dicks says:

      Hi Dayse,

      The listener needs to be running on the parent frame. The postMessage then sits on the child site and posts up to the parent, which then passes the information on.

      Hope this helps!