Create and Update Google Analytics Session Timeout Cookie

With this customTask trick, you can create a cookie which mimics Google Analytics' session timeout. This cookie is refreshed with every valid hit, and can be used to detect active/inactive sessions in the browser.

In Google Analytics, the concept of a session is the key aggregation unit of all the data you work with. It’s so central to all the key metrics you use (Conversion Rate, Bounce Rate, Session Duration, Landing Page), and yet there’s an underlying complexity that I’m pretty certain is unrecognized by many of GA’s users. And yet, since this idea of a session is so focal to GA (to the point of being overbearing), it’s annoying that the browser isn’t privy to any of the sessionization parameters that Google Analytics applies to the hits sent from the browser to its servers.

Well, to rectify this, I’ve written a very simple customTask solution, which basically mimics the session timeout (30 minutes by default) in a browser cookie. You can use this cookie for a number things, such as preventing event hits from being dispatched if the session has timed out, or for turning these event hits into non-interactive hits.

Naturally, I have also updated my guide to customTask as well as my customTask Builder tool with this trick, so you can start using it with all the other cool customTask tricks out there!

The customTask code

Here’s what the customTask method looks like:

var _customTask = function() {
  // Update expiresMs to be the number of milliseconds when the cookie should expire.
  // Update domain to match the parent domain of your website.
  var updateSessionCookie = {
    expiresMs: 1000*60*30,
    domain: 'mydomain.com'
  };

  // DO NOT EDIT ANYTHING BELOW THIS LINE
  var globalSendHitTaskName   = '_ga_originalSendHitTask';

  return function(customTaskModel) {

    window[globalSendHitTaskName] = window[globalSendHitTaskName] || customTaskModel.get('sendHitTask');

    customTaskModel.set('sendHitTask', function(sendHitTaskModel) {

      var originalSendHitTaskModel = sendHitTaskModel,
          originalSendHitTask      = window[globalSendHitTaskName];

      var hitType, nonInteraction, d;

      try {
        originalSendHitTask(sendHitTaskModel);

        // updateSessionCookie
        if (typeof updateSessionCookie === 'object' && updateSessionCookie.hasOwnProperty('expiresMs') && updateSessionCookie.hasOwnProperty('domain')) {
          hitType = sendHitTaskModel.get('hitType');
          nonInteraction = sendHitTaskModel.get('nonInteraction');
          if (nonInteraction !== true && (hitType === 'pageview' || hitType === 'event')) {
            d = new Date();
            d.setTime(d.getTime() + updateSessionCookie.expiresMs);
            document.cookie = '_session_' + sendHitTaskModel.get('trackingId') + '=true; expires=' + d.toUTCString() + '; path=/; domain=' + updateSessionCookie.domain;
          }
        }
        // /updateSessionCookie

      } catch(e) {
        originalSendHitTask(originalSendHitTaskModel);
      }
      
    });
  };
};

If you’re using Google Tag Manager, copy-paste that into a Custom JavaScript variable, and then change the first line to function() { and remove the very last character of the code block (the final semicolon). Then, add it as a new Field to Set in your Google Analytics tag settings.

if you’re using Google Analytics, you need to run this JavaScript before the Google Analytics snippet, and then when creating the tracker modify it to:

ga('create', 'UA-XXXXX-Y');
ga('set', 'customTask', _customTask); // <-- Add this
ga('send', 'pageview');

This customTask should be added to all the tags and hits that you want to have an impact on the session refresh. Basically, it should fire with all your pageviews and events, since those are the two that keep the session alive.

How it works

Any tag or hit that uses this customTask will now run some extra code when the hit has been sent to Google Analytics. The code checks whether the hit was interactive (since non-interactive hits don’t keep a session alive) and whether it was an event or pageview (since those are the two hits that keep a session alive).

In both these conditions are true, a cookie named _session_UA-XXXXX-Y is created (or updated if it already exists) with an expiration set to whatever you configured as the value of the expiresMs property in the beginning of the code block.

In other words, you will now have a cookie which, if it exists, means there is most likely an active session in Google Analytics. I say “most likely”, because there are many ways that sessions can be broken off even before the timeout expires.

This cookie is a reasonable abstraction of the session timeout schema in Google Analytics. It’s not perfect, but it gives you an idea.

Things you can do with it

Well, the easiest thing is to use it to prevent Events from firing if the session cookie doesn’t exist. Why? Because it’s tiresome having sessions that only have events. Even though there’s really no problem with having events fire before pageviews (even if all the documentation tries to tell you otherwise), there’s a risk you’ll have sessions that only have events.

Those sessions will be marred with the ugly (not set) Landing Page, because Google Analytics is tyrannical in demanding sessions to always include a pageview (I have no idea why).

So, in Google Tag Manager, you could do this by first creating a 1st Party Cookie variable for the session cookie. Remember to change the “UA-XXXXXX-Y” to whatever the tracking ID is for the property whose session timeout you are monitoring.

Then, create a new Custom Event trigger which checks if this cookie has the value true:

Now, any tag you add this trigger to as an exception will not fire if the session has timed out.

This is pretty brutal - you’re not sending event hits to GA because you’re afraid they’ll create orphaned sessions. It’s a valid fear to have with GA’s sessionization schema, but it might be overkill.

So another option is to turn all event hits into non-interactive hits, because these don’t increment the session counts or dimensions in your data set, but the data is still collected.

To do this, you need a Custom JavaScript variable that looks like this:

function() {
  return {{UA-XXXXX-Y session}} !== 'true';
}

This JavaScript returns false if the session is active, and true if there is no active session. By adding this to the Non-Interaction field in your Event tags, the hit will be non-interactive if there is no active session.

You could even modify the customTask to do this logic for you. Within the customTask method, just after try {, you could do something like this (you’ll need the 1st Party Cookie for this):

try {
  
  // ADD THIS
  if ({{UA-XXXXX-Y session}} !== 'true' && sendHitTaskModel.get('hitType') === 'event' && sendHitTaskModel.get('nonInteraction') !== true) {
    return;
  }
  // UP TO HERE
  
  originalSendHitTask(sendHitTaskModel);
  ...
}

This addition prevents the hit from being fired (and the cookie from being created/updated) if the session is not alive and the hit is an interactive event. For all other hit types, the hit gets sent and the cookie gets created. This way you don’t need to mess with triggers - you can have customTask do all the legwork for you.

Summary

This customTask trick can be used to bring some insight into the browser whether or not a session is currently active in Google Analytics.

It’s not perfect, since sessionization depends on so many things (of which a multitude happens in the bowel’s of GA’s processing servers), but it can be used as an indication of whether or not there’s an active session.

This solution is also not necessary, especially if you use BigQuery. The raw data you collect from the site is useful and valid even if there’s no pageview initiating a session. So make sure you have a use case for this before taking the plunge.