|
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
- Request a Confirmation Read Receipt
- Append a Query String key to a Beacon
Image
- 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
- Intercept that request
- Extract the key 'jdoe'
- 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
- Creating a class library project in
VS.NET
- Add a reference to System.Web
- 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.
- Request a Confirmation Read Receipt
- Append a Query String key to a Beacon
Image
- Implement a HttpModule to Track Reads
If you have any questions or comments about this, be sure to send them to
support@aspNetEmail.com
|