{"id":4033,"date":"2021-07-29T06:40:08","date_gmt":"2021-07-29T06:40:08","guid":{"rendered":"https:\/\/freshinbox.com\/blog\/?p=4033"},"modified":"2023-07-26T15:32:46","modified_gmt":"2023-07-26T15:32:46","slug":"how-to-build-your-own-email-open-tracking-pixel","status":"publish","type":"post","link":"https:\/\/freshinbox.com\/blog\/how-to-build-your-own-email-open-tracking-pixel\/","title":{"rendered":"Testing Apple\u2019s Pixelgeddon: How To Build Your Own Email &#8220;Open&#8221; Tracking Pixel"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">During this year\u2019s Apple WWDC conference, Apple announced an upcoming feature for iOS 15 that will be available for iOS devices called <\/span><a href=\"https:\/\/www.apple.com\/newsroom\/2021\/06\/apple-advances-its-privacy-leadership-with-ios-15-ipados-15-macos-monterey-and-watchos-8\/\"><span style=\"font-weight: 400;\">Mail Privacy Protection<\/span><\/a><span style=\"font-weight: 400;\">. One of the major features of Mail Privacy Protection is that images will be downloaded in the background \u4e00 even without the email being read by the recipient, rendering open tracking moot. This is because open tracking is based on having an image pixel being loaded when a recipient opens the email.<br \/>\n<\/span><\/p>\n<p>I wrote some observations on what I found out through this testing in this article:\u00a0<a href=\"https:\/\/freshinbox.com\/blog\/a-technical-take-on-ios15-mail-privacy-protection\/\">A Technical Take on iOS15 Mail Privacy Protection<\/a>.<\/p>\n<p><span style=\"font-weight: 400;\">If you\u2019re curious to find out yourself how Apple is implementing this feature, you\u2019re in luck.<\/span><\/p>\n<p><b>Update:<\/b> The tracking pixel code has been modified to support generating an external CSS pixel. <a href=\"https:\/\/proofjump.com\/blog\/detect-real-opens-on-apple-mail-using-an-external-css-pixel\/?from=freshinbox\">Read this article for more details<\/a>.<\/p>\n<h2><span style=\"font-weight: 400;\">Installing iOS 15 Beta<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Although iOS 15 won\u2019t launch until at least September, you can download the iOS 15 Beta on your phone if you&#8217;re curious. <u>A note of warning:<\/u> Installing Beta operating systems always carries a risk that it could mess up your device, so do not attempt this without first backing up your phone.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If you are going ahead with the Beta download, there are two ways to do this. The first is the official way: by signing up for <\/span><a href=\"https:\/\/www.cnet.com\/tech\/mobile\/ios-15-public-beta-download-and-install-apples-latest-software-on-your-iphone-right-now\/\"><span style=\"font-weight: 400;\">Apple\u2019s Beta program<\/span><\/a><span style=\"font-weight: 400;\">. The second, the unofficial but faster way, is to simply install the Beta profile into your phone by visiting and downloading the <\/span><a href=\"https:\/\/betaprofiles.com\/install\/ios-15\/\"><span style=\"font-weight: 400;\">iOS 15 Beta profile<\/span><\/a><span style=\"font-weight: 400;\"> from BetaProfiles.com.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Once you have iOS 15 Beta installed, you should see the prompt to turn on Mail Privacy Protection when you open up your mail app. Turn it on and you\u2019re good to go.<\/span><\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-4048\" src=\"https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/mpp.jpg\" alt=\"\" width=\"377\" height=\"524\" srcset=\"https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/mpp.jpg 400w, https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/mpp-216x300.jpg 216w\" sizes=\"(max-width: 377px) 100vw, 377px\" \/><\/p>\n<h2><span style=\"font-weight: 400;\">Pixel Trackers: Two Options<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">If you have access to a web server that runs PHP, you can run your own pixel tracker that will give you the most amount of information available. However, if you don\u2019t have access to a web server, you can also create a simple tracker using Google Analytics. You lose some fidelity (such as no IP or user agent), but you can get the time the pixels were loaded.<\/span><\/p>\n<h3><b>PHP script:<\/b><\/h3>\n<p><span style=\"font-weight: 400;\"><i>Note: this is a \u201cproof of concept\u201d and is not meant to be used as an industrial pixel tracker for millions of emails, as it will probably crash your server.<\/i><\/span><\/p>\n<p><span style=\"font-weight: 400;\">Below is a PHP script that will retrieve 3 things from a request and write to a file.<\/span><\/p>\n<ol>\n<li><span style=\"font-weight: 400;\"> The query string sent to it (using a predetermined parameter)<\/span><\/li>\n<li><span style=\"font-weight: 400;\">The IP address<\/span><\/li>\n<li><span style=\"font-weight: 400;\">The user agent string<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">Copy <a href=\"https:\/\/github.com\/freshinbox\/php-email-pixel\/blob\/main\/pixel.php\">this script<\/a> under your webserver directory and test it by visiting the URL (replace &#8220;\/path\/to&#8221; with the actual path your script is in):<br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\nhttps:\/\/your-server.com\/path\/to\/pixel.php<br \/>\n<\/span><br \/>\nDepending on your server setup, you might need to pre-create the pixel.log file and grant write permissions to your web server for the script to write to it. (The git source has the <a href=\"https:\/\/github.com\/freshinbox\/php-email-pixel\/blob\/main\/pixel.php\">latest version of this code<\/a>)<\/p>\n<pre class=\"wrap:true lang:default decode:true\">&lt;?php\ndate_default_timezone_set('America\/Los_Angeles');\n\nlog_request();\n\n\/* \n  Get the p \"parameter\" from the url\n  ie. https:\/\/myserver.com\/path\/to\/pixel.php?p=mypixel\n\n  If p is not set, print \"The pixel is live\" else output a gif pixel\n\n  NOTE: Make sure to only use https for the pixel:\n  <div class=\"video-container\"><blockquote class=\"wp-embedded-content\" data-secret=\"PNQw7CjW92\"><a href=\"https:\/\/proofjump.com\/blog\/chrome-update-blocks-non-https-images-in-email\/\">Next Chrome Update Will Block Non-HTTPS Images in Email<\/a><\/blockquote><iframe class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;Next Chrome Update Will Block Non-HTTPS Images in Email&#8221; &#8212; ProofJump Blog\" src=\"https:\/\/proofjump.com\/blog\/chrome-update-blocks-non-https-images-in-email\/embed\/#?secret=cF6fciJE6Q#?secret=PNQw7CjW92\" data-secret=\"PNQw7CjW92\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe><\/div>\n*\/\n$p = @$_REQUEST['p'];\nif(!$p){\n  die(\"The pixel is live!\");\n} else {\n  \/\/ https:\/\/stackoverflow.com\/questions\/3203354\/php-script-to-render-a-single-transparent-pixel-png-or-gif\/3203394\n  header('Content-type: image\/gif');\n  header('Cache-Control: no-cache, no-store, must-revalidate');\n  die(hex2bin('47494638396101000100900000ff000000000021f90405100000002c00000000010001000002020401003b'));\n}\n\n\/\/ Log the request as CSV\nfunction log_request(){\n  \/\/ You may need to pre-create pixel.log and set writable permissions\n  $logfile = \"pixel.log\";\n\n  $p = @$_REQUEST['p'] ?: 'not_set';\n  $date = new DateTime();\n  $dateval =  $date-&gt;format('Y-m-d H:i:s');\n  $ip = @$_SERVER['REMOTE_ADDR']; \/\/ Remove IP address\n  $agent = @$_SERVER['HTTP_USER_AGENT']; \/\/ User Agent\n  $referer = @$_SERVER['HTTP_REFERER']; \/\/ Referer\n\n  $list = [$dateval, $p, $ip, $agent, $referer];\n\n  $file = fopen($logfile,\"a\");\n  fputcsv($file,$list);\n  fclose($file);\n}\n?&gt;<\/pre>\n<p><span style=\"background-color: #ffffff; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 16px;\">If you have the URL correct, it should show:<\/span><span style=\"font-weight: 400;\">\u00a0 \u00a0\u201cThe Pixel is live!\u201d\u00a0<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\">And the pixel.log file should log your visit, as well.<\/span><\/p>\n<pre class=\"wrap:true lang:default decode:true \">\"2021-07-25 17:22:30\", not_set, 73.70.24.110, \"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/91.0.4472.77 Safari\/537.36\",\n<\/pre>\n<p><span style=\"font-weight: 400;\">Once you get that, you are ready to set up your pixel by appending a \u201cp\u201d parameter:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u00a0 \u00a0 https:\/\/your-server.com\/path\/to\/pixel.php<\/span><span style=\"font-weight: 400;\">?<strong>p=my_email<\/strong><\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Now you should just see a pixel (a 1&#215;1 gif image) when you load the URL in a browser &#8211; and your log should log the visit.<\/span><\/p>\n<pre class=\"wrap:true lang:default decode:true\">\"2021-07-25 17:25:22\", my_email, 73.70.24.110, \"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/91.0.4472.77 Safari\/537.36\",<\/pre>\n<p><span style=\"font-weight: 400;\"><b>Adding the pixel to your email:<\/b><\/span><\/p>\n<p><span style=\"font-weight: 400;\">Once this works, you are ready to embed your pixel into your email with the following code:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<pre class=\"wrap:true lang:default decode:true\">&lt;img src=\"https:\/\/your-server.com\/path\/to\/script\/pixel.php?p=my_email\" width=1 height=1 style=\"display:block;\"&gt;<\/pre>\n<p><span style=\"font-weight: 400;\">Once you embed the code, send the email, open it in an email client, and you should see the results in the log!<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><span style=\"font-weight: 400;\"><br \/>\nThe following are example user agent strings based on the kinds of email clients your email is opened in:<br \/>\n<\/span><\/p>\n<pre class=\"wrap:true lang:default highlight:0 decode:true\">iOS 13 iPhone prior to Apple's privacy pixel:\n\"Mozilla\/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit\/605.1.15 (KHTML, like Gecko) Version\/13.0.3 Mobile\/15E148 Safari\/604.1\"\n\n\niOS 15\/Apple Mail when images are proxied through Apple's servers:\n\"Mozilla\/5.0\"\n\n\nGmail:\n\"Mozilla\/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox\/11.0 (via ggpht.com GoogleImageProxy)\"\n<\/pre>\n<p><span style=\"font-weight: 400;\">As you can see other providers such as Gmail, Yahoo! Mail and Outlook.com also proxy their images.<\/span><\/p>\n<h3><strong>Google Analytics<\/strong><\/h3>\n<p><span style=\"font-weight: 400;\">With Google Analytics, you cannot get the IP address or User Agent string of the request, just the time the request was made. You might be saying, what&#8217;s the point of this? Well, it gives you the most important data: WHEN the pixel was retrieved.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This is a good way to tell when the Mail client loads your pixel in the background, even if you haven\u2019t opened the email. Read <a href=\"https:\/\/freshinbox.com\/blog\/a-technical-take-on-ios15-mail-privacy-protection\/\">this article<\/a> for more details \u4e00 but note it can take from a few minutes to several hours for the Mail client to download the pixel in the background, so be patient.<\/span><\/p>\n<p><b>Get your Google Analytics account set up:<\/b><\/p>\n<p><span style=\"font-weight: 400;\">Read<\/span> <a href=\"https:\/\/freshinbox.com\/blog\/tracking-interactions-in-email-with-google-analytics\/\">this article<\/a><span style=\"font-weight: 400;\"> to see how you can get your basic Google Analytics pixel set up.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><b>Configuring your pixel:<\/b><\/span><\/p>\n<p>For this pixel example, set the parameters as follows:<\/p>\n<p>tid=UX-XXXXX (replace with your analytics id)<br \/>\nec=pixel (event category)<br \/>\nea=open (event action)<br \/>\nel=my_email (event label)<\/p>\n<p>Your link would look like the following:<\/p>\n<pre class=\"wrap:true lang:default decode:true\">https:\/\/www.google-analytics.com\/collect?v=1&amp;tid=UA-XXXXX&amp;t=event&amp;cid=test&amp;cn=test&amp;cs=email&amp;ec=pixel&amp;ea=open&amp;el=my_email\n<\/pre>\n<p>You can check if your pixel is firing by putting the link into your browser and going to Google Analytics to view the &#8220;Realtime \/ Events&#8221; tab:<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-4053\" src=\"https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/analytics-event.jpg\" alt=\"\" width=\"718\" height=\"330\" srcset=\"https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/analytics-event.jpg 986w, https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/analytics-event-300x138.jpg 300w, https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/analytics-event-768x353.jpg 768w\" sizes=\"(max-width: 718px) 100vw, 718px\" \/><\/p>\n<p><span style=\"font-weight: 400;\"><b>Adding the pixel to your email:<\/b><\/span><\/p>\n<p>Add the following code to the bottom of your email.<\/p>\n<pre class=\"wrap:true lang:default decode:true\">&lt;img src=\"https:\/\/www.google-analytics.com\/collect?v=1&amp;tid=UA-XXXXX&amp;t=event&amp;cid=test&amp;cn=test&amp;cs=email&amp;ec=pixel&amp;ea=open&amp;el=my_email\" width=1 height=1 style=\"display:block;\"&gt;\n<\/pre>\n<p><b>Getting hourly pixel loading data<\/b><span style=\"font-weight: 400;\">:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Once you are capturing data, you\u2019d want to know when the events are actually coming in, which is the point of this exercise.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<p><span style=\"font-weight: 400;\">You can determine, the hour the pixel was loaded by loading the &#8220;Behavior \/ Events \/ Top Events&#8221; page on Google Analytics.<\/span><\/p>\n<p>Then select the &#8220;pixel&#8221; Event Category, followed by the &#8220;open&#8221; Event Action.<\/p>\n<p>Finally, select the &#8220;Secondary dimension&#8221; dropdown, select &#8220;Time&#8221; and pick &#8220;Hour of Day&#8221;.<\/p>\n<p>This will add a secondary column with values such as 2021072216 which translates to YYYYMMDDHH (Year Month Date Hour).<br \/>\nIn this case, the pixel was loaded between 4 (16) and 5pm on the 22nd of July, 2021.<\/p>\n<p>See the following screen capture for more details:<\/p>\n<p><span style=\"font-weight: 400;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-4103\" src=\"https:\/\/freshinbox.com\/blog\/wp-content\/uploads\/2021\/07\/analytics.gif\" alt=\"\" width=\"757\" height=\"555\" \/><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>During this year\u2019s Apple WWDC conference, Apple announced an upcoming feature for iOS 15 that will be available for iOS devices called Mail Privacy Protection. One of the major features of Mail Privacy Protection is that images will be downloaded in the background \u4e00 even without the email being read by the recipient, rendering open [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":4039,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2,19,28],"tags":[],"_links":{"self":[{"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/posts\/4033"}],"collection":[{"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/comments?post=4033"}],"version-history":[{"count":26,"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/posts\/4033\/revisions"}],"predecessor-version":[{"id":4268,"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/posts\/4033\/revisions\/4268"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/media\/4039"}],"wp:attachment":[{"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/media?parent=4033"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/categories?post=4033"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/freshinbox.com\/blog\/wp-json\/wp\/v2\/tags?post=4033"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}