How to set a UserID as a custom variable using the Google Analytics cookie

by Dan on March 15, 2014

I’m an active member of the LinkedIn Google Analytics group and a fellow group member, Ruth, recently asked how to set the unique Google Analytics user ID from the GA cookie as a custom variable.  I responded with the JavaScript code that would do this in classic Google Analytics.  Before long Ruth asked how to do this in Universal Analytics and finally in Google Tag Manager.  I thought it would make sense to post it here as well.

First off, why would you want to do this?  Setting a custom variable with the unique user ID from GA will allow you track individual user behavior in reports by building a custom segment against a particular user ID.  You can also use this user ID to connect non-authenticated user behavior (before a user creates an account on your site) to an authenticated user (after they create an account).  This can be immensely helpful in attribution.

How does this code work? 
The code snippets below all perform the same basic steps:

  1. Attempt to read the __utma cookie value.  (Read about the GA cookies in depth here)
  2. Make sure the cookie has data in it (not undefined).
  3. Split the cookie values into an array based on the dot delimiter.
  4. Extract the appropriate User ID from the array (2nd slot in classic GA, 3rd slot in Universal Analytics).  Note that JavaScript arrays are zero-based so the ‘cookieValues[1]‘ below  is really accessing the 2nd item of the array.
  5. Assign this User ID as a visitor scope custom variable in GA or as a custom dimension in Universal Analytics.
  6. Fires an event with a category of ‘Custom Variables’ and action of ‘Set UserId’ with non-interaction set to true.

Before getting into the code itself, a few assumptions are in order:

  1. You’re only firing one GA Profile ID on the site.
  2. You don’t already have a custom variable (custom dimension in Universal) in the first variable slot.  If you do, just modify the slot # to the first available slot.

This code needs to be implemented on your site after your Google Analytics code has had a chance to run.  This ensures that the __utma cookie that contains this unique user ID has been set on the user’s browser.  If you have your GA code running in the <head> section of your site, this code should be implemented near the closing </body> tag.

Here’s the script to set the GA user ID as a custom variable in the asynchronous version of classic GA:

Important Note: If you plan to upgrade to Universal Analytics in the near future, the functionality below will no longer work when you switch to Universal Analytics.  If you have not already implemented this User ID tracking, I would suggest implementing Universal Analytics first and then implementing the Universal Analytics logic in the 2nd script section below.

<script type="text/javascript"> 
function readCookie(name) { 
  name += '='; 
  for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) 
  if (!ca[i].indexOf(name)) 
    return ca[i].replace(name, ''); 
} 

var gaUserCookie = readCookie("__utma"); 

if (gaUserCookie != undefined) { 
  var cookieValues = gaUserCookie.split('.'); 

    if (cookieValues.length > 1) { 
      var userId = cookieValues[1]; 
      try {
        _gaq.push(['_setCustomVar',1,'gaUserId',userId,1]);
        _gaq.push(['_trackEvent', 'Custom Variables', 'Set UserId','',0,true]);
      } catch(e) {}
  }  
}  
</script>

If you do need to change the variable slot in the code above, change the blue value below to a number between 2 and 5 in the script above.

_gaq.push(['_setCustomVar',1,'gaUserId',userId,1]);

Universal Analytics:

In Universal Analytics, you are setting a custom dimension instead of a custom variable.  You’ll need to add the custom dimension in the Universal Analytics profile setup prior to implementing the code below.  If you already have a custom dimension in the first dimension slot (dimension1), you’ll need to change the ‘dimension1′ value in the code below to the appropriate ‘dimensionX’ value.

<script type="text/javascript"> 
function readCookie(name) { 
  name += '='; 
  for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) 
  if (!ca[i].indexOf(name)) 
    return ca[i].replace(name, ''); 
} 

var gaUserCookie = readCookie("_ga"); 

if (gaUserCookie != undefined) { 
  var cookieValues = gaUserCookie.split('.');
  if (cookieValues.length > 2 ) 
  { 
    var userId = cookieValues[2]; 
    try {
      ga('set', 'dimension1', userId);
      ga('send', 'event', 'Custom Variables', 'Set UserId', {'nonInteraction': 1});
    } catch(e) {}
   }  
}  
</script>

In Google Tag Manager with Universal Analytics:

You can implement the script below as a ‘Custom HTML’ tag firing on all pages (or just your landing pages, if preferred).  For the script below to work, you’ll also need to create an additional tag in GTM:

  1. Create a data layer macro to hold the userId value.
  2. Add an Analytics tag to set the custom dimension  in Universal Analytics passing in the userId macro you created above.  You can use a tag type of event to do this.  You’ll need to populate the event category and event action.  Be sure to set the non-interaction to ‘True’ to avoid artificially decreasing bounce rate.
  3. Set a firing rule for the tag of:  {{event}} equals setUserId
<script type="text/javascript"> 
function readCookie(name) { 
  name += '='; 
  for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) 
  if (!ca[i].indexOf(name)) 
  return ca[i].replace(name, ''); 
} 
var gaUserCookie = readCookie("_ga"); 
if (gaUserCookie != undefined)  { 
  var cookieValues = gaUserCookie.split('.');
  if (cookieValues.length > 2 )  { 
    var userId = cookieValues[2]; 
    dataLayer.push({'event':'setUserId', 'userId': userId}); 
  } 
} 
</script>

Have any implementation questions or comments?  Get in touch with me by leaving a comment below or on Google+.

  • Adam

    Wow. Thanks Dan. I’ve done a lot of searching for code snippets to do certain things, and this by far is the best write up. You go into detail about why you’d want to do it, and detailing what the code is doing in plain english, is like teaching a man to fish. I can teach myself what the code is doing because it’s detailed above it.

  • http://masterorganicchemistry.com James

    Hey Dan – what are your thoughts on the best way to deploy Universal Analytics in the Thesis theme (1.x )? I’ve tried two plugins with sub-par results. Direct email response more than OK

  • Pingback: Digital Analytics Week In Review - 21 March 2014 - Data Runs Deep()

  • http://www.creativesparkcolumbia.com Chip

    How does GA tie the two (before and after authentication) sessions together?

  • Axelle

    Would you not add a non-interaction boolean to the event string to avoid lowering your bounce count?

    Also interested if anyone has an answer to Chip’s question.

  • Axelle

    Just realised it was a data layer event, not a UA event, so please ignore my first question.

  • http://www.weismannweb.com Henry

    I thought the point was to set the user id value from your application not from the GA cookie?

  • http://DataFray.com Matt

    This is a really great post, I have found a few in the past that explained how to do this with classic GA but this is the first I have found that explains (in depth) all 3 options. I am in the process of creating a WordPress Plugin for a client that incorporates this code and auto populates a hidden form field (on every form) with the UserID and will allow them to better track form engagement… Thanks

  • Dries

    Hey Dan,

    Was wondering, why you would check the utma cookie first?
    You could just write your userid out of the database, right? Why bother? Or am I missing out on something?
    You’re using the utma cookie to check unique id’s on a cookie base, but.. does it really matter?
    You could just write a general user-id for everyone that hasn’t been logged in (if you’re not checking the client-id I mean).
    So you’re not making your own coded userid? why?

  • Guilherme Avila

    How can i do to setup a session custom variable using tag manager?

    • Shamtaro

      I think you can just add session count as a secondary dimension and get unique session IDs that way if that’s what you’re looking for.

  • alex

    I think the userId (in fact cid) is conformed of: cookieValues[2] + ‘.’ + cookieValues[3], isn’t it?

  • http://www.glasgoweb.co.uk will

    Hi Dan

    I followed your instructions for Tag setup but the event will not fire as dimension1 is undefined
    on reviewing using debug the data layer is showing the correct userid but cookie inspector shows undefined

    I have tired to set the Priority so UA first with dimension, then your script but I still get undefined

    any ideas where my error is

    • Dan

      Do you have a test page I can review?

      • http://digitalimpact.co.uk/ Will Craig

        Hi Dan thanks for getting back to me its really appreciated!

        here is my test page

        http://digitalimpact.co.uk/

        here is a screen of what i see in tag manager
        http://prntscr.com/5m6ndn

        HTML CODE
        http://prntscr.com/5m6okp

        Macro
        http://prntscr.com/5m6oqs

        event
        http://prntscr.com/5m6oxq

        rule
        http://prntscr.com/5m6p6i

        • Dan

          Hey Will,

          In your firing rule, you currently have “{{userId}} equals setUserId”. This needs to be changed to: “{{event}} equals setUserId”. The rest looks perfect!

          In your dataLayer.push function, you are passing an event equals setUserId:
          dataLayer.push({‘event’:'setUserId’, ‘userId’: userId});

          - Dan

          • http://digitalimpact.co.uk/ Will Craig

            Hi Dan

            thanks that worked perfectly, what a great blog post

            keep up the great work

            will

  • Patrick

    If the variable is renamed to gaUserId is the event still setUserId or do I need to change the event name ?

  • Shamtaro

    I’ve just implemented this on a site – working great! I’m seeing User numbers match up perfectly, however I’m missing sessions and pageviews (about -2% less than what shows in reporting). Anyone have any ideas how this can be happening?

  • http://androidappsapkdownload.com/ Android Apk File Download

    We are Looking to get logged in User Information for Our website : http://mcx.freetips.tips here we have all AGENTS who Logged in and Provides the Trading Call during Market timming.. so its so benefits if we have logged in information in our analytics. Thanks for Post

  • Erez

    Hey
    Is it actually possible to extract the User Id, this solution extract the first part of the CID (client Id)
    I want to extract the user id from the login.

    Thank you for a great post !

  • Iago Rodríguez-Quintana

    Hello @Dan, I have implemented your solution. Thanks for it. Just for confirmation, it only sets User Id on the second visit and further right? I mean, the first visit of a given visitor won’t pass the sentence “if (gaUserCookie != undefined)” right?

    • http://dan-russell.com danruss

      Hi Iago, it only sets user ID on the second activity (pageview, event, or yes – a new session).

      - Dan

      • Iago Rodríguez-Quintana

        Thanks Dan -@danruss:disqus-. And would you happen to figure out how to make it work from the first visit? I want to achieve this because I am interested in “marking” each visit (not only recurrent) so I can trace what any given visitor did, instead of having aggregated statistics.

      • Peter Becci

        How can you get it to set the userID on the initial visit?

  • Andy Miller

    Hi Dan. Thank you very much for your post.

    I’ve been chipping away at setting up your script for several months now. I’m wondering if you could help troubleshoot what I’m doing wrong. I’ve included some screen shots below.

    I’ve implemented these tags using tag manager on pollen.com.au.

    • http://dan-russell.com danruss

      Hey @disqus_UPazp7j3YD:disqus,

      You’re really close! It looks like you may have the firing rules swapped for the ‘User ID Script’ and your GA tag. You want to trigger the ‘User ID script’ on window load and the GA event on the ‘Set UserId’ event. You want the sequence to look like this:

      (trigger) Window load ->
      (tag) User ID Script ->
      (trigger) Data Layer push with event = setUserId and the UserId value ->
      (tag) Non-interaction GA event which assigns the UserId value to the user

      Does that help?

      • Andy Miller

        Hi Dan. Thanks for the swift response!

        That Window Load trigger in my previous screen shot is actually one from another tutorial that I was experimenting with to pass the Client ID through also. I’ve attached it here to double check it matches what you’re referring to.

        I’ve also just updated my tag setup with (I hope) what you have described above.

  • Ken Whiteman

    Hi Dan,

    Great post! I’m trying to tie offline conversions (phone orders) to the website visitor for advertising optimization. I’ve created a system that looks at IP address and products they’ve looked at to identify the correct visitor to caller relationship. Anyway, once I have the google client id for the visitor, I want to send it up to UA to tie the transaction to the visitor. I’m sending the transaction and product variables through a datalayer variable to GTM that looks something like the following:

    dataLayer = [{
    'store': 'admin site',
    'pageType': 'transaction',
    'event':'transaction',
    'supportId': '".strtoupper($support_id)."',
    'ecommerce': {
    'purchase': {
    'actionField': {
    'customerID': '".$customers_id."',
    'id': '".$order_number."',
    'affiliation': 'Phone Order',
    'callType': '".$dial_in_type."',
    'revenue': '".$revenue."',
    'tax':'0.00',
    'shipping': '".$shipping_charge."',
    'actualShipping': '".$actual_shipping."',
    'COGS': '".$total_product_cost."',
    'profit': '".$profit."',
    'coupon': '".json_encode(utf8_encode($coupon))."',
    'discount': '".$discount."',
    'processed_by': '".$processed_by."',
    'action': 'purchase'
    }
    ".$products."
    ]
    }
    }
    }];

    It is firing on a pageview event. The variables are being recorded properly and the data is showing up in Analytics. However, I need to tie in the google cid and marry the sessions. Since I’m not pushing the data through an individual push statement, how would I do it? Just by adding:

    ‘userId’='$google_cid’,

    to the list? Does Analytics already know what to do with userId, or does it need to be configured through a custom dimension? Been at this for a while and hoping you might be able to help. Cheers!

    • http://dan-russell.com danruss

      Hi @ken_whiteman:disqus ,

      You’ll need to add a custom dimension in UA for ‘userId’. When you setup the custom dimension, be sure to set the scope to User. Once you’re passing in the userId, you’ll be able to filter against it in the GA UI or in the API.

      Let me know if you’re able to get it working.

      - Dan

  • Lazer Mangel

    Hi Dan,

    Thanks for the awesome post! I’m working on a website that as part of the registration process must send visitors to a third party site. After they complete the registration on the third party site, they are redirected but to my site for a final step in the registration process. I can easily track our visitors and how many click over to the third party site, but I am trying to find an effective way to track those same users when they return so that I can properly determine my conversion rate. Would this set up work?

  • Slava M.

    Hi Dan! Thanks for your post. I’ve been looking for it so long. I have an issue maybe you can assist me with it.
    I need to implenet the structure you’ve described above throu several domains a.site.com b.site.com c.site.com
    I want to know for example if user comes to a.site.com and after a couple of months goes to b.site.com and submit form what was his first source / medium. Can you please assist? How to set your code correctly with cross-domain tracking.
    Thanks for your time!

Previous post:

Next post: