_setVar() and the zero bounce rate bug in Google Analytics

UPDATE - Jan 27, 2009: Google has reportedly fixed the bug in GA that was causing the problem described on this page. Thus the fix is no longer necessary, but I will leave it here for historical reference.

Google Analytics has a _setVar() function that lets you assign a single user-defined variable to be passed to your reports, which you can then see (and filter on).

Unfortunately, using _setVar() on each page seems to break the bounce rate calculation in GA. This is because _setVar() apparently records a second pageview when it is called, meaning a visitor who left after their first pageview didn't actually 'leave' in the eyes of GA because they had a second page view secretly recorded via _setVar(). Thus the bounce rate in Google Analytics drops to zero (or near zero) because every single visitor is getting hit with two pageviews.

Conveniently, the value that you assign to _setVar() is stored by GA in the _utmv cookie, and the tracking code actually reads this cookie and passes back the value to Google Analytics, which records it for every single pageview, regardless of whether you set it with _setVar() on that particular pageview. Therefore you don't need to call _setVar() unless you actually want to change the user defined value. In fact, you shouldn't call it any more often than you have to, because you don't want to be creating these extra pageviews.

This lends itself to a nice solution to the problem: we'll simply check if the value we want to call _setVar() with is already in the _utmv cookie. If it is, we'll skip calling _setVar().

/*
 * getCookie(): given a cookie name, get its value. Returns null if the cookie can't be found.
 * From http://www.webreference.com/js/column8/functions.html
 *
 */
function getCookie(name) {
  var dc = document.cookie;
  var prefix = name + "=";
  var begin = dc.indexOf("; " + prefix);
  if (begin == -1) {
    begin = dc.indexOf(prefix);
    if (begin != 0) return null;
  } else
    begin += 2;
  var end = document.cookie.indexOf(";", begin);
  if (end == -1)
    end = dc.length;
  return unescape(dc.substring(begin + prefix.length, end));
}
 
/*
 * utmvCookieCheck(): given a value, read the __utmv cookie and see if
 * that value is already set. Return true if so, false otherwise.
 *
 */
function utmvCookieCheck(value) {
	var utmvCookie = getCookie("__utmv"); 
 
	if (utmvCookie == null)
		return false;
 
	// get rid of the Google's domain prefix ID, which appear on all
	// GA cookies
	utmvCookie = utmvCookie.replace(/^\d*\./, '');
 
	return (utmvCookie == value) ? true : false;
}

Now, just wrap your call to _setVar() with a call to check if the cookie exists first:

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-XXXXXX-X");
var userDefinedValue = 'CHANGE_ME_TO_YOUR_DESIRED_VALUE_FOR_SETVAR';
if(!utmvCookieCheck(userDefinedValue)) {
	pageTracker._setVar(userDefinedValue);
}
pageTracker._trackPageview();
</script>

That's it. Now _setVar() will only be called if the value isn't already set.