mail for ASP.NET email smtp mail for ASP.NET email smtp
 
    
    

Email Tracking

Tracking Email Reads using ASP.NET

Download The C# Source Code
Download The VB.NET Source Code

 

A number of customers and people have emailed me asking how to track when an email is opened and read. This article will cover various techniques for tracking when an email is read, along with some of the pro and cons of each technique.

There are three common techniques for tracking when an email is read. These techniques include

  1. Request a Confirmation Read Receipt
  2. Append a Query String key to a Beacon Image
  3. Implement a HttpModule to Track Reads

Request a Confirmation Read Receipt

The first technique deals with requesting a confirmation read receipt. A confirmation read receipt is a special header tag that gets added to the email. When the email client (such as Outlook, Outlook Express, Eudora ) reads the read receipt header, an email is generated and sent back to the sender. If you are using aspNetEmail, a code example for doing something like this is:

[C#]

            EmailMessage msg = new EmailMessage( "mail.MyCompany.com" );
            msg.FromAddress = "me@MyCompany.com";
            msg.To = "you@YourCompany.com";
            msg.ConfirmRead = true;


[Visual Basic]

            Dim msg As New EmailMessage("mail.MyCompany.com")
            msg.FromAddress = "me@MyCompany.com"
            msg.To = "you@YourCompany.com"
            msg.ConfirmRead = True

Although this technique is easy to implement, there are a number of problems with it.

First: is that not all email clients support the confirmation read receipt header. Thus, a user may open a your email and read it, but you never get the read receipt email.

Second
: of those email clients that do support it, most users have it turned off, because this is a technique used by spammers. Again, you will not get the confirmation emails.

Third: is that sometimes the user gets presented with a security dialog, stating the sender wants a read receipt. Some receivers view this as an invitation to their privacy, and will decline the dialog, and may unsubscribe from your newsletter.

Fourth: The Read Receipt would still need to be checked, and this would require an automated process scanning an inbox or a folder.

Append a Query String key to a Beacon Image

Please Note:
These image techniques will only work if the recipient’s mail client allows images to be displayed, and allow http requests to remote web servers for downloading images. It is entirely possible to read email in its text only form, and thus never know when an email has been read.

Another popular technique is to embed a transparent beacon image into the HTML text of the email. For example, your raw HTML email text may look like

<img alt="Company Logo"  src="http://www.mysite.com/images/companylogo.gif"><br>
Hi John Doe,<br>
<p>Here is the status of your daily stock quotes.
<br>... more data here
<br>... more data here
<img width=1 height=1 src="http://www.mysite.com/images/transparent.gif?key=jdoe">

From this HTML text, you can see a 1x1 transparent gif was embedded. When this email is read, a request will be made to www.mysite.com for the image found at images/transparent.gif. When this request is made, the querystring key=jdoe will be appended to the IIS logs. Thus the IIS logs can be analyzed for query string image keys, and the database can be updated with all the emails that have been read. Although this technique is user friendly there are two main issues with it.

First
: You have to parse the IIS logs. Although this can be done, sometimes it become a tedious and time consuming task.

Second: This is a known spamming technique. And because it is commonly used by spammers, more spam filters are preventing the request of beacon images. Therefore your email will get read, which is still good, but the the image request will not be recorded in the IIS logs.

Implementing a HttpModule to Track Reads

The last technique is more advanced, and more powerful to use than either of these techniques. This technique implements a HttpModule to intercept a request to an image embedded in your email, such as your company logo, and records that request to the database. But before we get to that part, lets talk about HttpModules for a minute.

What is a HttpModule?

If you are familiar with ASP.NET, you may have heard of, or even written a HttpModule. A HttpModule is a component that can be plugged directly into the HTTP processing pipeline of ASP.NET. This makes HttpModules very powerful, because we can manipulate requests and responses as they enter or exit IIS. For some more links about HttpModules you may want to check out the MSDN article at http://msdn.microsoft.com/msdnmag/issues/02/05/asp/default.aspx or else some examples at http://www.123aspx.com/search.aspx?lookfor=httpmodules&wording=1

Our HttpModule will manipulate the request as it enters IIS.

Using the HttpModule

Our module will intercept an image that will actually be named the email key. The email key is unique to each email, and somehow relates back to the receiver of the email. In the previous example, the key was appended as the query string. In this example, the image is actually named the key. Also, instead of a transparent beacon, this image will actually be our company logo. So, using the HTML text of the previous email, the HTML  now looks like

<img alt="Company Logo" src="http://www.mysite.com/images/jdoe.aspx"><br>
Hi John Doe,<br>
<p>Here is the status of your daily stock quotes.
<br>... more data here
<br>... more data here
 

NOTE:
It is important to note that the file jdoe.aspx DOES NOT EXIST ON THE FILESYSTEM. Our HttpModule will be built in such a way that it intercepts the request for the file jdoe.aspx, and will record this request to the database. Then, instead of returning a 404 (file not found response), our HttpModule will actually send the company logo back instead. This is described in more detail below. Using a HttpModule makes it appear the file jdoe.aspx (or any .aspx file found below our images directory) actually exists.

So when the email client requests http://www.mysite.com/images/jdoe.aspx, our HttpModule will perform three functions. It will

  1. Intercept that request
  2. Extract the key 'jdoe'
  3. Return the actual company logo, which is actually named "myRealLogo.gif".

Coding our HttpModule

Lets start writing our HttpModule. The first thing you will need to do, is create a HttpModule assembly. Start by

  1. Creating a class library project in VS.NET
  2. Add a reference to System.Web
  3. Implement the IHttpModule interface.

All of the code listed below is in C#. For a VB.NET listing, use the link found at the top of the article to download the VB.NET code

You code should look like this starting out:

using System;
using System.Web;
namespace ImageTracker
{
 public class TrackRequest1 : IHttpModule 
 {
  public TrackRequest1()
  {

  }
  public void Dispose() 
  {
  }
  public void Init(System.Web.HttpApplication Appl) 
  {
  }
 }
}

In the Init() method, we are going to wire up a our event named GetImage_Request. Our code will change to

  public void Init(System.Web.HttpApplication Appl) 
  {
   Appl.BeginRequest+=new System.EventHandler(GetImage_BeginRequest);
  }
  public void GetImage_BeginRequest(object sender, System.EventArgs args) 
  {
  }

Now that you've registered the event (delegate) GetImage_BeginRequest, we can start adding some real code to our module. After its done, the code will look like (we will discuss the code in detail below):

using System;
using System.Text.RegularExpressions;
using System.Web;
using System.Data.SqlClient;
namespace ImageTracker
{
 public class TrackRequest1 : IHttpModule 
 {
  private string pattern = @"/images/(?<key>.*)\.aspx";
  private string logoFile = "~/images/theRealLogo.gif";
  private string dbConnectionString = "server=(local);database=Northwind;trusted_connection=yes;";
  public TrackRequest1()
  {

  }
  /// <summary>
  /// Required by the interface IHttpModule
  /// </summary>
  public void Dispose() 
  {
  }

  /// <summary>
  /// Required by the interface IHttpModule
  /// Wires up the BeginRequest event.
  /// </summary>
  public void Init(System.Web.HttpApplication Appl) 
  {
   Appl.BeginRequest+=new System.EventHandler(GetImage_BeginRequest);
  }

  /// <summary>
  /// Extracts the email key from the url and saves to the database. Also serves up the image
  /// </summary>
  /// <param name="sender">HttpApplication</param>
  /// <param name="args">Not used</param>
  public void GetImage_BeginRequest(object sender, System.EventArgs args) 
  {
   //cast the sender to a HttpApplication object
   System.Web.HttpApplication application =(System.Web.HttpApplication)sender;

   string url = application.Request.Path; //get the url path

   //create the regex to match for beacon images
   Regex r =new Regex( pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase );
   if( r.IsMatch( url ) )
   {
    MatchCollection
mc =   r.Matches( url );
    if( ( mc != null ) && ( mc.Count > 0 ) )
    {
     string key = ( mc[ 0 ].Groups[ "key" ].Value );
     SaveToDB( key );
    }

    //now send the REAL image to the client
    application.Response.ContentType = "image/gif";
    application.Response.WriteFile(  application.Request.MapPath( logoFile ) );

    //end the resposne
    application.Response.End();
   }

  }

  /// <summary>
  /// saves the key to the database
  /// </summary>
  /// <param name="key">key database value</param>
  private void SaveToDB( string key )
  {
   if( ( key==null) || ( key.Trim().Length == 0 ) )
    return;

   //normally you would use a stored procedure, but the sql statement is written out for simplicity
   string sqlText = string.Format( "INSERT INTO EmailTracker ( emailKey ) VALUES ( '{0}' )", key.Replace( "'", "''" ) );
   SqlCommand cmd = new SqlCommand( sqlText, new SqlConnection( dbConnectionString ) );
   cmd.Connection.Open();
   cmd.ExecuteNonQuery();
   cmd.Connection.Close();

  }

/**
   Database Table Schema
   
  CREATE TABLE [EmailTracker] (
  [primaryKey] [int] IDENTITY (1, 1) NOT NULL ,
  [emailKey] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
  [dateEntered] [datetime] NULL CONSTRAINT [DF_EmailTracker_dateEntered] DEFAULT (getdate()),
  CONSTRAINT [PK_EmailTracker] PRIMARY KEY  CLUSTERED 
  (
   [primaryKey]
  )  ON [PRIMARY] 
 ) ON [PRIMARY]
 GO



**/


 }
}

Lets discuss this code in more detail.

Every time a request is made to our application, GetImage_BeginRequest() is called. Once inside of GetImage_BeginRequest(), we check to see the path of the requesting endpoint. For example http://www.mysite.com/images/jdoe.aspx

We then run our Regular Expression pattern against it, to see if we are trapping for that file type (which, in this example we are). Because it returns true, the Regex then grabs the email key, which is named "key". That key is then passed to the method SaveToDb(). SaveToDb() simply save the key to a database table. Once that key is saved, we can go back at a later date to determine which emails were read. Now that we've saved the key to the database, all that's left is to grab the real company logo, and send it to the browser. This is accomplished with

    application.Response.ContentType = "image/gif";
    application.Response.WriteFile(  application.Request.MapPath( logoFile ) );   //where logoFile is defined above

    
//end the resposne
    application.Response.End();
 

Deployment

Once you've compiled your HttpModule into an assembly, all that's left to do is deploy it to the /bin directory of your website, and to register it. To register the HttpModule you will need to add the following entries to the web.config:

  <system.web>
    <httpModules>
     <add type="ImageTracker.TrackRequest1,ImageTracker" name="ImageTracker" />
    </httpModules>
 </system.web>

Go ahead and browse to http://www.mysite.com/images/whatever.aspx or http://www.mysite.com/images/jdoe.aspx  (you will need to substitute mysite.com for your actual site) and you will see that "whatever" and "jdoe" will be added to the database.

Conclusion

That's all there is tracking email reads and the successfulness of your campaign. In this article, you've looked at the following three techniques for tracking when an email is read.
 

  1. Request a Confirmation Read Receipt
  2. Append a Query String key to a Beacon Image
  3. Implement a HttpModule to Track Reads

If you have any questions or comments about this, be sure to send them to support@aspNetEmail.com

 

The box is not shipped.
aspNetEmail is a    
downloadable product.
 
aspNetEmail
Voted Number 1 ASP.NET Email Control
Voted Best Email Control


aspNetEmail
Runner Up ASP.NET Email Control
Voted Runner Up Email Control


aspNetEmail
Voted Number 1 ASP.NET Email Control
Voted Best Email Control


aspNetEmail
Voted Number 1 ASP.NET Email Control
Voted Best Email Control


aspNetEmail
Voted Number 1 ASP.NET Email Control
Voted Best Email Control


aspNetTraceRoute
Voted Runner Up Networking Control
Runner Up - Networking Control