Universal Analytics can collect Page Timing data from users that load your pages. This data is populated in to the Behavior -> Site Speed -> Page Timings report, and it’s a very useful feature for optimizing your website.

However, there’s a murky underside to this generous feature. The way Page Timings collection works is that when Pageview hits are sent from the site, a sample of these (1% by default) are automatically followed by a timing hit which includes page performance data grabbed from the Navigation Timing API.

So far so good. This is how it’s supposed to work. If you’ve set siteSpeedSampleRate to 100, the first pageview request of every page will be automatically followed by this timing hit.

However, the timing hit copies its information from the pageview hit. Basically, all the Custom Dimensions and Custom Metrics are copied from the pageview. Luckily, things like Enhanced Ecommerce metadata are not copied into the timing hit.

As you can see, the timing hit below the pageview has the same Custom Dimension and the same Custom Metric as the Pageview hit. This is annoying, especially if you use hit-scoped Custom Dimensions or pretty much any type of Custom Metrics. This is data inflation that you can’t control.

So we need a way to reset Custom Dimensions and Custom Metrics for the timing hit.

I am grateful to Clément Simon for helping me come up with the following solution.

Solution: customTask

If you’ve been reading my recent articles, you might have seen this coming. I consider customTask to be one of the most versatile features recently added to analytics.js.

We’ll use customTask to check if the hit type is a timing hit, and if it is, we’ll make sure no Custom Dimensions and Custom Metrics are sent with the hit.

analytics.js

This is what the situation is with analytics.js before you make the change:

ga('create', 'UA-12345-1', {siteSpeedSampleRate: 100});
ga('set', 'dimension1', 'My Value');
ga('send', 'pageview', {metric1: 20);

This creates a tracker, sets a Custom Dimension, and then sends a pageview request with a Custom Metric. Since you’re sampling Page Timings at 100%, the pageview hit will be instantly followed by a timing hit that contains these two custom definitions, too.

To fix it, this is what you’ll do:

ga('create', 'UA-12345-1', {siteSpeedSampleRate: 100});
ga('set', 'dimension1', 'My Value');

ga('set', 'customTask', function(model) {
  var tempFieldObject = {};
  var i = 1;
  if(model.get('hitType') === 'timing') {
    while (i !== 201) {
      tempFieldObject['dimension' + i] = undefined;
      tempFieldObject['metric' + i] = undefined;
      i++;
    }
    model.set(tempFieldObject);
  }
});

ga('send', 'pageview', {metric1: 20});

This clears all the possible Custom Dimensions and Custom Metrics from the timing hit, and thus data inflation is avoided.

Google Tag Manager

To fix this in Google Tag Manager, you’ll need a Custom JavaScript Variable, and you’ll need to add a new field in your Page View tag.

The Custom JavaScript Variable has the following code:

function() {
  return function(model) {
    var tempFieldObject = {};
    var i = 1;
    if(model.get('hitType') === 'timing') {
      while (i !== 201) {
        tempFieldObject['dimension' + i] = undefined;
        tempFieldObject['metric' + i] = undefined;
        i++;
      }
      model.set(tempFieldObject);
    }
  };
}

And then you add it to your tag like this:

Whichever method you use, the customTask implementation will purge all Custom Dimensions and Custom Metrics from the automatically generated timing hit.

Summary

This was just a quick tip to fix a potential data integrity issue on your site. Granted, it’s rare for you to run into problems with the Custom Dimension duplication (unless you use it against the ga:hits metric), but on an aggregate level the Custom Metrics inflation can be troublesome.

This isn’t the only thing where the Site Speed Sample Rate can wreak havoc on your data. With Google Tag Manager and “virtual” pageviews, you might be inflating your page timing data by a great deal, and I’ve written about how to solve this issue here.

Note also that these are client-side fixes to something that, in my opinion, should be prevented by analytics.js to start with. I don’t understand why the Page Timing hit just copies everything from the pageview. I’d want it to be configurable, at the very least.