Universal Analytics: Fire Script Just Once per Session

Guide for creating a cookie that emulates Google Analytics sessions. The purpose is to use it to delimit specific Google Tag Manager tags to fire only once per GA session.

There is a new version of this post for GTM V2 here.

While going over my previous post about using weather conditions to segment data in Google Analytics, I started thinking about performance issues. Since I’m using a visit-scope custom dimension, it seems futile to have it send the weather details with every single page load. The odds of the weather changing drastically during one visit are slim (unless you live in the UK), and I have yet to come up with a good reason to change my on-site behavior because the weather changed from a drizzle to a downpour.

With Universal Analytics, it’s not possible to mine session data from the cookie set by the service. This is because the custom client ID set by the cookie is “later used by Google Analytics servers to calculate visitor, session, and campaign data.” (emphasis mine; see the developer guide). This is relevant because I want my custom JavaScript to only fire once, at the beginning of a session (visit). I have no way to use the _ga-cookie since, as stated above, it calculates session data in the bowels of Google Analytics servers.

To overcome this problem, I create a custom session cookie, which mimics the default properties of the Google Analytics cookie mainly in that it expires after 30 minutes of inactivity as well.

Do note that this is not a 100 % accurate emulation of how Google Analytics calculates sessions. The cookie doesn’t take into account changing campaign sources which, by default, always initiate a new session in Google Analytics. Preliminary research shows that the accuracy is around 95 %, though I’m getting some missed hits to the Open Weather API as well, which means that the cookie is actually even more accurate.

How it works

I’m trying to be generic here, but I will use a real-life example from the weather script I created previously.

Here’s how the implementation basically works:

  1. When the visitor comes to the site, my script searches for a cookie named “session” using Google Tag Manager’s Session macro

  2. If the cookie exists, it means that a session is still active and no code is executed but for a reset of the cookie expiration time

  3. If the cookie doesn’t exist, it means that the session has expired, and in this case I run the weather code and then create the cookie

If you try to create a cookie which already exists, it overwrites the original cookie. This is why I can use the same cookie setup code for both (2) and (3) above.

Since it’s a first-party cookie, and since it’s set on the root path of the site (/), there’s no risk of overwriting any other cookies (unless some other site plugin has a cookie named “session”).

My original weather tracking solution fired on every single page load. This creates quite a burden on the API as well as site performance (since it might take almost half a second for the API script to fire). My new code looks like this:

  if (typeof({{Session alive}}) == "undefined") {
    // Weather tracking code here
  var d = new Date();
  var expires = "expires="+d.toGMTString();
  document.cookie = "session=1; "+expires+"; path=/";

As you can see, the old code is only run if the macro {{Session alive}} is undefined, i.e. the cookie doesn’t exist.

Creating the macro is dead simple:

  1. Create new macro called “Session alive”

  2. Set Macro Type 1st Party Cookie

  3. Set Cookie Name “session”

So what you do here is create a macro which returns the “session” cookie value. Whenever this macro is referenced to in code, you are actually using the cookie value.

When I poll for this cookie in the code above, I’m actually only interested in whether this cookie exists or not. That is why I use the if(typeof({{Session alive}}) == “undefined”) structure. If the cookie doesn’t exist, the macro has no reference, which means that it is an undefined structure.

Finally, regardless of whether the cookie was found or not, a cookie named “session” is created with an expiration date 30 minutes in the future (1 800 000 milliseconds later).

Pros and cons

Well obviously, having the cumbersome API call fire just once per session is a victory for site performance and for “best practices” analytics. Because I already have a visit-scoped dimension, it would be counter-intuitive to send it with every single page view hit.

The main problems, as I see them, have to do with maintenance. If you have a custom expiration time for the GA session, you need to remember to modify the expiration date (now 1800000 milliseconds) in the “session” cookie as well, if you want it to mimic Google Analytics performance. Also, if you already have a cookie named “session” firing on your site, you need to change this one’s name.

One more problem was discussed in the opening paragraphs of this post. This is not a 100 % accurate emulation of Google Analytics session computation, so you will have a slight margin of error in the number of visits with weather as a dimension vs. the number of visits to your site. Feel free to explore how to make calculating a GA session even more accurate, and drop me a line if you find something interesting :)

I would really like it if I could bind my code to the actual _ga-cookie, but as far as I know, that’s not possible.

EDIT: See this post on sending custom dimensions with events for a smarter implementation of the idea above.