Last updated 4 September 2018

If you have been reading my blog articles over the past year, you might have noticed a disturbing trend. I’ve published 9 articles on customTask since the API was released. It might not sound like much, but I can’t think of a single feature in Google Analytics or Google Tag Manager that has so completely convinced me of its usefulness in such a short time.

The customTask API is a feature of the Universal Analytics library (used by Google Tag Manager’s tags, too). It lets you get and set values from and to the (Measurement Protocol) hit as it’s being generated. This is really useful for a couple of reasons, which I’ve covered in the previous articles, but I’ll go over them briefly in the beginning of this guide, too.

Suffice to say that especially for Google Tag Manager, customTask adds a lot of value. With GTM, it’s always been quite difficult to access the hit-building process when using tag templates. Luckily, customTask offers a solution to this problem, and at least for this particular developer it’s opened a whole new world of Google Analytics customization.

However, in spite of writing all those articles on customTask, I recently realized that I never actually took the time to explain more thoroughly how it works. Also, I haven’t yet shared solutions for combining multiple customTask tricks in a single tag. This guide seeks to address these points.

Be sure to check out my customTask Builder tool as well - it will help you compile the necessary JavaScript for customTask to run in your setup without conflicts!


The Simmer Newsletter

Subscribe to the Simmer newsletter to get the latest news and content from Simo Ahava into your email inbox!

How Tasks work

When you run the ga('send', 'pageview') command either directly in your site JavaScript or indirectly via Google Tag Manager’s Google Analytics tags, you actually request that the Universal Analytics JavaScript library (analytics.js) compile an HTTP request to the Google Analytics servers.

The endpoint to which the hits are sent is typically /collect on the Google Analytics collector domain. The process of building and sending hits to the GA servers is also known as Measurement Protocol (MP). In fact, MP is the underlying process used by all GA tracking mechanisms, be they the ga('send'...) JavaScript SDK, Google Tag Manager’s GA tags, native SDKs for Android and iOS, and custom-built HTTP requests from point-of-sales systems, for example.

When you use the JavaScript SDK, either via ga('send', 'pageview') or the Google Analytics tags in GTM, you are thus initiating a sequence of processes, where the final product is the actual MP request to Google Analytics. This sequence comprises a number of tasks, of which customTask is the first one that is executed.

Here’s a quick recap of what each task does, listed in the order they are applied to the hit.

Task Description
customTask No functionality of its own, but can be used to manipulate the model object before it is processed by the other tasks.
previewTask If the request is generated from the pageview of a “Top Sites” thumbnail in Safari, abort the hit.
checkProtocolTask Aborts the request if the page protocol is not valid (http or https).
validationTask Checks if the fields in the request have valid and expected values. Aborts the request if this is not the case (e.g. trying to send a string as the Event Value).
historyImportTask If there is still legacy tracking running on the site, this task imports information from the old GA library cookies into Universal Analytics. Very useful if the site is migrating to Universal Analytics.
samplerTask If you’ve decided to manually sample the users to stay within Google Analytics’ processing limits, this task aborts the request if the user is sampled out of data collection.
buildHitTask Generates the hitPayload string, which is essentially the list of query parameters and their values passed to the Measurement Protocol request.
sendHitTask Sends the hitPayload in a Measurement Protocol request to the GA servers.
timingTask If you are automatically sampling pages for page speed measurements, this task sends the timing hit to Google Analytics.
displayFeaturesTask If you have enabled display features in GA settings, this task compiles the request to the DoubleClick servers.

Each tasks receives the model object as a parameter. The model object contains all the fields that have been set in the tracker, as well as any enhancements applied in the tasks themselves.

The beauty of customTask is that because it runs before any of the other tasks, you can use it to overwrite behavior of these other tasks. For example, if you want to run Google Analytics locally or in a Chrome Extension, you’ll need to make sure checkProtocolTask is never run, because it aborts the hit builder if the page protocol is not http or https. The customTask would look like this:

var customTask = function(model) {
  // Prevent checkProtocolTask from running
  model.set('checkProtocolTask', null);

As you can see, the task is actually a field in the model object that you can manipulate just as you can manipulate any other field in the model. For example, if you want to grab the tracking ID (UA-XXXXXX-Y) and send it in the Custom Dimension with index 15, you can use the following customTask:

var customTask = function(model) {
  // Get the tracking ID from the model object
  var trackingId = model.get('trackingId');
  // Set the tracking ID to Custom Dimension 15
  model.set('dimension15', trackingId);

To see what fields are available in the model object, check this developer documentation. Note that it’s not complete, since it’s missing all the tasks.

By running these model interactions with customTask, you have a lot of control over how the task sequence is run. Especially with Google Tag Manager, it would be difficult to run any complicated logic for when and how to manipulate sendHitTask, if you applied it directly to the tag as a field.

How to add customTask to your hits

To add customTask to your Google Tag Manager tags, you need to create a Custom JavaScript variable which returns the customTask method in the variable body. Practically all my customTask articles show examples of what this Custom JavaScript variable looks like.

When you are ready to add the variable to your tags, you can do it either via a Google Analytics Settings variable (recommended), or by overriding the tag settings directly.

You need to scroll down to Fields to set, and add a new field which looks like this:

If using the on-page Universal Analytics (analytics.js) snippet, you add the customTask like this, for example:

var _customTask = function() {
  // Set Client ID to Custom Dimension 199
  return function(customTaskModel) {
    customTaskModel.set('dimension199', customTaskModel.get('clientId'));

ga('create', 'UA-12345-1');
ga('set', 'customTask', _customTask());
ga('send', 'pageview');

Do note that you can only set one customTask per tracker / hit / tag. If you want to add more than one “trick” to the customTask function, you need to write the JavaScript that combines all the different solutions into one coherent whole.

Or, you can use my customTask Builder tool, which does the dirty work for you.

Things you can do with customTask

As written above, customTask is special for two reasons.

  1. It has no special functionality of its own. A task queue can and will run perfectly without customTask defined.

  2. It runs before any other task, meaning you have full control over how the queue is processed.

With these two things in mind, here’s the list of all the customTask tricks I’ve written about, with a description of what makes them special.

1. Set Client ID as a Custom Dimension

Link to article: #GTMTips: Use customTask To Access Tracker Values In Google Tag Manager.

This is one of the simpler tricks. It doesn’t manipulate the task queue itself at all, it simply gets the Client ID from the model object, and then sets it as the value of the specified Custom Dimension.

It’s an elegant solution to a difficult problem. Without customTask, getting and setting the Client ID in a Google Analytics tag was difficult. If the tag is run for a first-time visitor who hasn’t received a Client ID yet, the actual clientId field is not available when the regular tag fields are resolved. A typical hack for this solution was to fetch the Client ID by creating a dummy tracker or by sending the Client ID with an Event tag that fired after the Pageview.

But with customTask, there’s no need for such unorthodox methods.

2. Duplicate Google Analytics tags to more than one property

Link to article: #GTMTips: Send Google Analytics Tag To Multiple Properties.

This is customTask at its purest. The customTask function first stores a reference to sendHitTask in a global variable, after which it overwrites sendHitTask in the current model object with the duplicator code.

The duplicator first lets the original hit fire to GA, after which it replaces the Tracking ID in the hitPayload with the Tracking ID of the second property. Then, the global sendHitTask is invoked again, and the hit is sent to the second property with an identical payload.

Note! The reason the reference to sendHitTask is stored in a global variable is to avoid issues with field multiplication. Let’s say you always use this to get the original sendHitTask reference:

var originalSendTask = model.get('sendHitTask');

Each time this customTask code is run, it will get a reference to what is stored in the model object’s sendHitTask. Unfortunately, when building standard Ecommerce hits, or when automatically sampling timing hits, the tag is automatically sent more than once. Thus the reference to the original sendHitTask is recursively augmented with each iteration of model.set('sendHitTask');, resulting in code being executed erratically.

By having a global variable store the reference to the original sendHitTask, this recursive problem is avoided.

3. Track offline users in Google Analytics

Link to article: Track Users Who Are Offline In Google Analytics.

This is all David Vallejo. His solution tackles the problem of lost internet connectivity and hits that are aborted because of this. It uses customTask to check if the user is offline, and if they are, the hits are stored in a batch queue. Once connectivity is restored, the queue is processed, and the hits are finally sent to GA.

4. Remove PII from GA payloads

Link to article: #GTMTips: Remove PII From Google Analytics Hits.

With GDPR coming soon, covering all your bases with regard to private data is a good idea. Fixing potential PII leaks before they hit Google Analytics servers is a good method as any to make sure you’re complying with Google Analytics’ Terms of Service as well as the increasingly stricter regulations for sending and collecting personal data.

Here, customTask takes the hit payload before it is sent to GA, and purges it of all matches against any given regular expressions. These regular expressions can be built to match things like email addresses and social security numbers.

5. Fix site speed sampling messing with your Custom Metrics

Link to article: Prevent Site Speed Sampling Rate From Skewing Custom Dimensions And Custom Metrics.

This is a fairly simple trick, but it fixes a potential issue with Custom Metric data being skewed by the automatically collected page timing hits. The issue is that the timing hit copies all parameters from the Page View tag that is being sampled, and thus any Custom Dimensions and Custom Metrics are sent more than once, too.

Here, customTask is attached to the Page View tag, where it checks if the hit type is timing (due to the automatically generated timing hit), and if it is, it removes all references to Custom Dimensions and Metrics from the request.

6. Respect opt-out

Link to article: #GTMTips: Respect Opt-Out From Advertising And Analytics.

A simple trick, again. This time customTask checks if the user has specific opt-out settings in the browser, and if they do, it aborts the requests to Google Analytics and DoubleClick. You’ll need to manually create the setting itself, be it a pop-up that stores the opt-out in a browser cookie or something, but once you’ve done it, this solution makes it easy to enact the opt-out programmatically.

7. Send details about Optimize experiments to Google Analytics

Link to article: Send Event And Custom Dimension If Google Optimize Experiment Is Running.

This trick tackles an issue with how Google Optimize data is being reported in Google Analytics. Basically, the Experiment Name and Experiment ID dimensions are scoped to the user for the duration of the experiment. Thus it’s not possible to get a segment of the actual sessions when the user was actively participating in an experiment page.

The customTask checks for certain features of the Page View tag it is attached to, and if these features are found, it updates a Custom Dimension in the hit payload with details about the experiment. You can then segment your GA data with these Custom Dimensions to see sessions where the user was actually active on an experiment page.

Link to article: #GTMTips: Auto Link Domains With Regex.

With this trick, customTask brings Google Tag Manager to feature parity with analytics.js. Regular, on-page Google Analytics tracking lets you use regular expressions with the Auto-Link Domains plugin. Google Tag Manager only accepts string values.

Here, customTask actually applies the Auto-Link plugin to the tracker object itself, passing the array of regular expressions to the plugin. This way, domains can be automatically linked with cross-domain parameters based on a regular expression match, rather than an exhaustive list of strings.

9. Send Google Analytics payloads to Snowplow

Link to article: #GTMTips: Automatically Duplicate Google Analytics Hits To Snowplow.

I’m a huge fan of Snowplow, mainly because it’s open-source and tackles many difficult issues having to do with sessionization and tracking schemas. This trick uses customTask to replicate what the “official” Snowplow Google Analytics plugin does.

Google Tag Manager doesn’t support adding plugins to tags, so using customTask to replicate plugin features is as good a solution as any.

10. Send Google Analytics payload length as a Custom Dimension

Link to article: Send Google Analytics Payload Length As Custom Dimension.

This tip lets you collect the payload length of each Google Analytics request as a Custom Dimension in Google Analytics.

The maximum length of a Google Analytics payload is 8192 bytes. It’s useful to check if you’re approaching this value with some of your hits, because if the payload length exceeds this, the hit is never sent to Google Analytics.

Be sure to check my tip below on how to automatically reduce the payload length.

11. Send Hit Type as a Custom Dimension

Link to article: #GTMTips: Add Hit Type As A Custom Dimension.

With this trick, you can send the hit type (e.g. pageview, event, timing) as a Custom Dimension with every hit to which this customTask is attached.

Very useful for debugging, since you can now build segments where you directly refer to the hit type, which is not available (surprisingly) as a default dimension in Google Analytics.

12. Automatically reduce payload length

Link to article: Automatically Reduce Google Analytics Payload Length.

With this customTask, the payload sent to Google Analytics is automatically parsed and, if necessary, reduced to go under the maximum payload length of 8192 bytes.

It’s very typical especially with Enhanced Ecommerce to send very long payloads to Google Analytics. Unfortunately, if these payloads exceed the maximum length, the hits are never sent and key data will thus be missing from your data set.

You can use this trick to recursively parse the payload and drop unnecessary parameters from it, which only serve to waste valuable space under the length cap.

Link to article: Create And Update Google Analytics Session Timeout Cookie.

This trick creates (or updates) a browser cookie which expires after a timeout you have configured (30 minutes by default).

Each hit that uses this customTask will refresh the cookie very time the hit is fired.

Thus, if this cookie exists in the browser, it’s an indication that there might well be a Google Analytics session active for the current user.

You can use the cookie to block event hits from being dispatched to Google Analytics if there is no active session, for example.

14. Tracking cross-domain iframes

Link to article: Tracking Cross-Domain Iframes - Upgraded Solution.

Here we’ll update an older solution of mine by using customTask and a window.setInterval() polling script together to great effect.

The idea is that when the hit is built, the customTask looks for the given iframe on the page, and if it’s found within a configured timeout, the iframe is reloaded with cross-domain tracking parameters.

This is a more elegant way to approach the problem of cross-domain iframes, because it skips past the race condition of running the script before the iframe has been loaded on the page, and it also uses the correct tracker object instead of just the first tracker on the page.

15. Prevent duplicate transactions from being sent

Link to article: Prevent Duplicate Transactions In Google Analytics With customTask.

Again an update to an old trick.

In this solution, customTask stores all sent TransactionIDs in the browser. Once a transaction hit is queued for dispatching to GA, the customTask checks if the hit’s Transaction ID is found in this list of IDs that have already been sent. If a match is made, the hit is blocked, thus preventing duplicated data from being sent to GA.

If no match is made, the hit is sent normally, and the Transaction ID is written into the list of sent IDs, so that any future attempt to send a transaction with this ID will be blocked.

Contrary to the older solution, this upgraded method will block Enhanced Ecommerce as well. For Standard Ecommerce, you can (and should) still use the customTask, but you’ll still need to use triggers and exceptions. This is all detailed behind the link above.

16. Obfuscate and duplicate Google Analytics data

Link to article: Obfuscate And Duplicate Google Analytics Data.

This is a rather complex little script which duplicates all the Google Analytics data to which the customTask is added to, and dispatches it to the Tracking ID you provide in the configuration.

The catch is that in addition to duplicating it, the data is also obfuscated. All the strings are converted to words in a finite set, and this is done predictably (so any given string is always converted to the same word). All prices are also modified by a randomized percent value.

The point is that you can use this to create a demo or training GA data set out of any actual and real data set without revealing where the data comes from.

17. Use localStorage for Client ID persistence in Google Analytics

Link to article: #GTMTips: Use localStorage For Client ID Persistence In Google Analytics.

Here, I tackle the latest attack on cookie storage by Apple: Intelligent Tracking Prevention 2.1.

By using localStorage to save the Client ID, you can circumvent the policy of limiting cookies set with document.cookie to a maximum expiration of 7 days.

It’s not perfect, and it’s definitely more a tech demo rather than a fully functional solution to solve your enterprise web analytics woes, but the functionality should be sound. There are some hiccups with regard to cross-domain tracking, so remember to revisit the article if you find that something is broken - I’m going to try to keep it up-to-date if Google decides to update the cross-domain linker parameter format, for example.

Putting it all together (literally)

NOTE! I’ve built a tool which you can use to compile the necessary JavaScript automatically. You’ll still want to read through this chapter, though, as it explains why building the script can be a chore.

If you like customTask as much as I do, chances are you’ll want to use more than one trick listed above (and elsewhere) in your tags. There’s a catch though. You need to respect the basic features of JavaScript, mainly:

  • You can only set a single field once per tag, so you can only set one customTask field per tag.

  • You can only set a single model parameter once per tag, so you can only set one sendHitTask attribute per tag, for example.

So, let’s say you want to do both tricks (2) (Send GA hit to more than one property) and (4) (Remove PII from GA hits). You might be tempted to try this:

function() {
  return function(model) {
    // Specify the PII regular expressions
    var piiRegEx = [{
      name: 'EMAIL',
      regex: /.{4}@.{4}/g
    var globalSendTaskName = '_' + model.get('trackingId') + '_originalSendTask';
    var originalSendTask = window[globalSendTaskName] = window[globalSendTaskName] || model.get('sendHitTask');
    // Clear the payload from PII:
    model.set('sendHitTask', function(sendModel) {
    // Duplicate the hit to another tracking ID:
    model.set('sendHitTask', function(sendModel) {

Unfortunately, this would not work. You are setting the sendHitTask field twice, meaning the second one is the only one that counts (since it overwrites the first set).

Instead, you need to apply some JavaScript chops and combine both of these solutions in the same sendHitTask. It’s not exactly trivial, and there’s no textbook method for doing it. You’ll need to figure out the order in which the code should be processed.

In this example, we want the PII purge to happen before any hits are sent or duplicated. So the final code would look something like this:

function() {
  return function(model) {
    // Specify the PII regular expressions
    var piiRegEx = [{
      name: 'EMAIL',
      regex: /.{4}@.{4}/g
    // Specify the GA tracking ID of the property to which you want to duplicate the hits
    var newTrackingId = 'UA-12345-2';
    var globalSendTaskName = '_' + model.get('trackingId') + '_originalSendTask';
    var originalSendTask = window[globalSendTaskName] = window[globalSendTaskName] || model.get('sendHitTask');
    var i, hitPayload, parts, val, oldTrackingId;
    model.set('sendHitTask', function(sendModel) {
      // Clear the payload of PII:
      hitPayload = sendModel.get('hitPayload').split('&');
      for (i = 0; i < hitPayload.length; i++) {
        parts = hitPayload[i].split('=');
        // Double-decode, to account for web server encode + analytics.js encode
        val = decodeURIComponent(decodeURIComponent(parts[1]));
        piiRegEx.forEach(function(pii) {
          val = val.replace(pii.regex, '[REDACTED ' + + ']');
        parts[1] = encodeURIComponent(val);
        hitPayload[i] = parts.join('=');
      sendModel.set('hitPayload', hitPayload.join('&'), true);
      // Rewrite the tracking ID
      hitPayload = sendModel.get('hitPayload');
      oldTrackingId = new RegExp(sendModel.get('trackingId'), 'gi');
      sendModel.set('hitPayload', hitPayload.replace(oldTrackingId, newTrackingId), true);

The code is fairly complex, and complexity increases with each distinct trick you want to run. This is an unfortunate consequence of the limitations of JavaScript and the fact that there is only one customTask to manipulate.

In the end, it’s really up to your JavaScript skills. You’ll need to be aware of how the browser runs the code line-by-line, and thus you’ll need to make sure that whatever you want to run first is also executed first by the browser. That’s why in the example above I have the PII purge running before the hit duplication. If the PII purge happened AFTER the hit is duplicated, it would be redundant, since it would still allow PII to potentially flow to Google Analytics.


As I wrote in the beginning, customTask is one of the nicest features to emerge in Google Analytics in a long time. With Google Tag Manager, it’s a real powerhouse of customization.

It’s a good idea to read about the Task queue in general. Understanding how tasks work will give you great insight into how analytics.js compiles and dispatches hits to Google Analytics. Once you have a good grasp on all the different moving parts of the Tasks queue, you should be able to come up with cool customTask solutions of your own, which you can then share in the comments of this article, for example!