Track dwell time with Google Tag Manager

Updated: Sep 15, 2020

This is one the hacks I love the most about Tag Manager, discovering bounce rates from users coming from Google search results. In other words, users coming from Google dwell on your page and then go back to a search result by clicking the back button on their browsers.

This metric is different than bounce rate, because bounce rate can be referred to any traffic source, not just Google organic traffic. And also bounce rate can be manipulated on Google Analytics, hence it's not a ranking signal.

Dwell time instead is referred only and exclusively to Google as traffic source and cannot be manipulated. It can be, however, measured. This is a great indication of relevancy between what users search, click and then find on your landing page. So, in theory, the longer users stay on your landing page (without going back to SERPs), the more relevant your result was for Google.

This hack is very complex to implement so but it works if done right. The idea for this post came to from a case study I am presenting at SMXL Milan 2018, where I speak about the link between user experience on the landing page and increased rankings on Google. During my preparation for the case study, I have read an experiment back in 2015 from Rand Fishkin about dwell time. Google must have been taking this metric very seriously because if users go back to search results very quickly, the result wasn't relevant for them.

So the metric that everyone said cannot be measured, but only guessed, made me think.

What if it can be measured?

And I wanted to start measuring it to have better reports on Google Analytics. Luckily we got Tag Manager that allows us to do just this: measure dwell time.

Challenges with tracking dwell time

One of the most challenging problems with this metric is that, for tracking it, we would need to know where the user go after he clicks the back button on his browser: but we don't have any clue where it goes. This is your browser security right there.

The other problem is the back button on the browser: we can't measure that either because the button is not on your web page, it's outside of it.

Luckily I am not the first one to encounter - and try to solve - these problems as Simo Ahava resolved them before me with an amazing, yet complex, hack. So I am going to thank Simo and use his JavaScript for tracking users' browser history.


We hereby create the first of 2 tags to setup our hack.

  1. Go on Tag Manager

  2. Click new Tag

  3. Choose Custom HTML Tag

  4. Name it "GA - Dwell time - HTML"

  5. Insert the JavaScript code provided below

  6. Click Save


(function() {

var s =;

var h = document.location.hash;

var e = {{Event}};

var n = {{New History Fragment}};

var o = {{Old History Fragment}};

// Only run if the History API is supported

if (window.history) {

// Create a new history state if the user lands from Google's SERP

if (e === 'gtm.js' &&

document.referrer.indexOf('') > -1 &&

s.indexOf('gclid') === -1 &&

s.indexOf('utm_') === -1 &&

h !== '#gref') {

window.oldFragment = false;


} else if (e === 'gtm.js') {

window.oldFragment = true;


// When the user tries to return to the SERP using browser back, fire the

// Google Analytics timing event, and after it's dispatched, manually

// navigate to the previous history entry, i.e. the SERP

if (e === 'gtm.historyChange' &&

n === '' &&

o === 'gref') {

var time = new Date().getTime() - {{DLV - gtm.start}};

if (!window.oldFragment) {


'event' : 'returnToSerp',

'timeToSerp' : time,

'eventCallback' : function() {




} else {








This is a simple double-check to make sure the variables are ticked.

You also need Page URL and Event variables ticked.


In this step you learn how to instruct Tag Manager to track a browser history event, that we can call popstate.

Trigger number 1

  1. Go to trigger

  2. Create New Trigger

  3. Name it : Event - History Change with gref

  4. Trigger type: History Change

  5. Trigger Fires on: Some history changes

  6. From the Drop Down menu choose "New History Fragment"

  7. Second Drop Down: Matches Regex and then ^$

  8. From the Second Drop Down menu choose "History Source"

  9. Drop menu next to it: equals and then popstate

  10. Save

This Trigger only launches when a browser history event is detected which is also a popstate, and the new history fragment is empty.

Trigger number 2

The second trigger is a default All Pages trigger. This is the screenshot:


Create the following Data Layer Variables.

Variable 1: This one stores the time when the GTM container snippet was executed.

Variable 2: This is where the time spent on the landing page is stored.


With this trigger, you instruct Tag Manager to fire a custom event that is the ReturnToSerp, which is pushed via the Custom HTML Tag (we have built this before).

Also, we have instructed Tag Manager not to start a new session if the user stays longer than 30 minutes, which is the default time for a session to expire in Google Analytics.