Third & GroveThird & Grove
Feb 21, 2014 - Justin Emond

Creating a Tracking Pixel with Drupal

For a recent client project we need to track whether a user registering on the site had recently seen an ad promoting registration. If the user clicked on the ad and immediately registered we wouldn’t have to do anything beyond standard Google Analytics custom campaign tracking codes.

This is an easy problem for cookies to solve, with one wrinkle. Let’s say our registration form was on spacetowel.com and the ad would run on luxurycement.com. Because of the cross domain security cookie policy, cookies set by one domain can’t be read by another. (This prevents you from making a site that grabs the unique session IDs for other sites you don’t control.)

Since we can’t read a cookie set by luxurycement.com, spacetowel.com needs to serve an asset on luxurycement.com. Enter the tracking pixel, stage right. A tracking pixel is most commonly a transparent 1x1 gif. Since we are serving a HTTP request for the client’s browser, to load our image, we can write a cookie that we can later read. Once the cookie is created it’s trivial for us to check for it during registration and track along with that registration if the user had seen the ad.

Serving a tracking pixel with Drupal is very easy. First we register a custom page callback:

/**
 * Implements hook_menu().
 */
function dm_tracking_pixel_menu() {
  $items = array();
 
  $items['track.gif'] = array(
    'page callback' => 'dm_tracking_pixel_gif',
    'file' => 'dm_tracking_pixel.pages.inc',
    'access arguments' => array('access tracking pixel'),
  );
 
  return $items;
}

 

This would make spacetowel.com/track.gif return an image from Drupal. The magic happens in dm_tracking_pixel_gif(). This routine was based on an excellent example on Stack Overflow.

 

<?php
 
/**
 * Page callback for showing a simple tracking pixel.
 */
function dm_tracking_pixel_gif() {
  ignore_user_abort(true);
 
  // Ensure the tracking pixel is always served by Drupal so the cookie is set.
  drupal_page_is_cacheable(FALSE);
 
  // Store the last time they saw an ad.
  $cookie_data = array();
  // Set some data you want to store here, and then set the cookie.
  user_cookie_save($cookie_data);
 
  if (function_exists('apache_setenv')) {
    apache_setenv('no-gzip', 1);
  }
  ini_set('zlib.output_compression', 0);
  if (ob_get_level() == 0) {
    ob_start();
  }
  drupal_add_http_header('Content-encoding', 'none');
  drupal_add_http_header('Content-type', 'image/gif');
  drupal_add_http_header('Content-Length', '42');
  drupal_add_http_header('Cache-Control', 'private, no-cache, no-cache=Set-Cookie, proxy-revalidate');
  drupal_add_http_header('Expires', 'Wed, 11 Jan 2000 12:59:00 GMT');
  drupal_add_http_header('Last-Modified', 'Wed, 11 Jan 2006 12:59:00 GMT');
  drupal_add_http_header('Pragma', 'no-cache');
  // This is the actual image, no GD library required!
  echo sprintf('%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%',71,73,70,56,57,97,1,0,1,0,128,255,0,192,192,192,0,0,0,33,249,4,1,0,0,0,0,44,0,0,0,0,1,0,1,0,0,2,2,68,1,0,59);
  ob_flush();
  flush();
  ob_end_flush();
 
  drupal_exit();
}

 

Ensure that your ad team adds a call to the tracking pixel (in the form of an HTML image tag) in your ads, and you are set.

Update January 2017: Here is a helpful guide to the new Google URL Builder.