Complete GTM Solution for Internal Traffic Self-Identification

Quick Introduction

Blogging in the midst of the Corona outbreak, we recently outlined a set of filtering strategies in Detecting Internal Traffic in Adobe Analytics in the Time of COVID-19. The examples we used were geared towards Adobe Analytics users, but most are in fact applicable to any kind of tracking implementation.

We next realized that this problem is so pervasive that the analytics community may benefit from a complete solution that is flexible enough to cover a good range of use cases.

We picked Google Tag Manager for several reasons. One is its popularity, of course. Another reason, as of April 2020, the new Google Analytics App + Web platform still has no native ability to filter out internal traffic. This makes the pain of filtering out internal traffic even more acute for GTM + GA users.


Solution Outline

The steps below detail how to provide both desktop and mobile browser users with a quick and easy way to self-identify as internal traffic. Any subsequent activities from their browser will be muted completely until they clear all their cookies.

After thinking long and hard, we decided that the self-identification approach (which can be easily combined with other methods) is the most flexible and universal one. While it does rely on end user cooperation, it has the added benefit of enabling filtering across a wide range of devices that users may hop on and off of. It's relatively easy to roll out, and it's the only approach that users on private mobile devices are likely to be able to apply and re-apply quickly.

By making self-identification user-friendly—just press and hold for 3 seconds along the top edge of any site page—we're hoping that word of mouth in the organization is sufficient to reach a healthy percentage of remote users.

The solution has the added benefit of placing no burden on IT, leaving resources free to work on deploying other approaches that are more baked into devices that the organization has control over.


Implementation Steps:

1. Create a GTM variable based on a Data Layer var name. In this example, both vars are named "internal":


2. Create a trigger that we can later use to block any tags set to fire at "Page View - DOM Ready":


3. Create a Custom HTML tag set to fire on all pages, as early as possible, at "All Pages - Page View":


4. Copy-paste the code below into the HTML field of the custom tag: 

(function() {

    var storageKeyName = '_internal';
    var varName = 'internal';
    var msg = "OK, Internal.";

    var dataLayer = window.dataLayer;
    var pressHoldDuration = 3 * 1000;
    var el = document;
    var timer;
    el.addEventListener("mousedown", pressing, false);
    el.addEventListener("mouseup", notPressing, false);
    el.addEventListener("touchstart", pressing, false);
    el.addEventListener("touchend", notPressing, false);
    function pressing(e) {
      var y = e.touches ? e.touches[0].clientY : e.clientY;
      if (y > 30)
      timer = setTimeout(setInternal, pressHoldDuration);
    function notPressing(e) {
      //console.log("Not pressing!");
    function setInternal(e) {
      !window.localStorage || window.localStorage.setItem(storageKeyName, true);
      //console.log("localStorage key set:", storageKeyName);
      var node = document.createElement("div");
      node.style.position = 'fixed';
      node.style.padding = node.style.left = node.style.top = "10px";
      node.style.background = 'rgba(0, 0, 0, 0.1)';
      node.style.zIndex = 999999;
      node.style.color = 'rgba(255, 255, 255, 0.7)';
      var text = document.createTextNode(msg);
      setTimeout(function() { document.body.removeChild(node); }, pressHoldDuration);
    if (window.localStorage && window.localStorage.getItem(storageKeyName)) {
        var data = {}; data[varName] = true;
     	!dataLayer || dataLayer.push(data); 
        //console.log("localStorage key was found and placed in dataLayer:", varName);




5. Use the trigger you configured at step 2 to block any tags set to fire at "DOM Ready".

If you need to block tags at "Window Loaded" or at some custom event, you will need a separate blocking trigger, or a modification to your existing firing trigger.

In the example below, we are using the blocking trigger to cut off the Firebase App + Webb tracking completely for any users self-identifying as "Internal":


6. Test your implementation:
  • Pressing and holding the mouse (or finger) for 3 sec (configurable at step 4) along the top edge of any page should trigger the following message:

  • From that moment on, you should be seeing a message entering your dataLayer soon after "Page View" that sets "internal" to true for your browser. This message will normally persist until this browser's cookies are cleared:

  • If at any point you wish to reset your Internal status, the easiest way is by deleting the flag in F12 > Application > Local Storage. You may want to clear the flag to confirm that browsers without "_internal" (name configurable at step 4) have no dataLayer message and will still fire all tracking:


Advanced Tips:

  • GTM managers with a need to block tags fired as early as "Page View" may wish configure a custom variable that doesn't rely on the dataLayer message and instead reads the _internal storage key directly:

function() {

   return window.localStorage ? window.localStorage.getItem('_internal') : false;


  • Those of you who are conversant in JavaScript will notice that the solution can be easily adapted to fire only when a certain page element is pressed and held. You just need to point the var el to the right element and remove the 30-pixel boundary check in the function pressing. Obviously, this element either has to be available very early in the page load, or else be created by the code itself:


    var el = document.getElementById('#myelid');
    function pressing(e) {
      //var y = e.touches ? e.touches[0].clientY : e.clientY;
      //if (y > 30)


Tags: Data Quality Google Analytics Tips

Subscribe to our quarterly data quality newsletter