Gmail blocking small embedded inline images in email template

Google refuses to show images with data url in Gmail's web interface. It's a known problem (in Google's view a security measure) for a long time highly criticized.

The good news is that you have another option except for using external images.

Using an inline attachment with a Content-ID works with Gmail.

After adding your images as inline attachments you'll need to point CID URLs instead of Data URLs in the html body.

There are plenty of modern libraries that will allow you to send emails with inline attachments easily. But I wrote a sample script in VBScript with CDO library, ready to use if you have a box Windows 2000+ installed.

Let's prepare the test environment.

Put the files in a directory as in the screenshot below.

enter image description here

tpl.html is the template file you gave. You'll need to make some changes in this file.

Replace both img elements with the following respectively. Note that data urls gone. image1 and image2 are the Content IDs specified for each inline attachments in the script. Nothing associated with file names here.

<img src="cid:image1" alt="SpaceImage" title="Space Image" style="display: block" width="225" height="126" />
<img src="cid:image2" alt="HostImage" title="Host Image" style="display: block" width="225" height="225" />

Embedded.vbs:

MsgBox "Wait while your email is being sent.", vbOKOnly Or vbInformation

'************ CONFIGURATION *************
Const smtpUsername = "..."
Const smtpPassword = "..."
Const smtpHost = "smtp.sendgrid.net"
Const smtpPort = 587
Const senderEmail = "...@..."
Const recipientEmail = "[email protected]"
'************ CONFIGURATION *************

Const cdoRefTypeId = 0

Set Fso = CreateObject("Scripting.FileSystemObject")

Set objMail = CreateObject("CDO.Message")
With objMail.Configuration
    .Fields("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
    .Fields("http://schemas.microsoft.com/cdo/configuration/sendusername") = smtpUsername
    .Fields("http://schemas.microsoft.com/cdo/configuration/sendpassword") = smtpPassword
    .Fields("http://schemas.microsoft.com/cdo/configuration/smtpserver") = smtpHost
    .Fields("http://schemas.microsoft.com/cdo/configuration/smtpserverport")  = smtpPort
    .Fields("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    .Fields("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 20 'secs
    .Fields.Update
End With

With objMail
    .AutoGenerateTextBody = False
    .From = senderEmail
    .To = recipientEmail
    .Subject = "Inline Image Test"
    .BodyPart.ContentTransferEncoding = "quoted-printable"
    .BodyPart.Charset = "utf-8"
    
    ' Adding images as inline attachments with Content IDs which is used with image sources. e.g. <img src="cid:image1" .. >
    .AddRelatedBodyPart Fso.GetAbsolutePathName("image1.jpg"), "image1", cdoRefTypeId
    .AddRelatedBodyPart Fso.GetAbsolutePathName("image2.jpg"), "image2", cdoRefTypeId
    
    'append html body from file
    .HTMLBody = Fso.OpenTextFile("tpl.html").ReadAll
    .Send
End With

MsgBox "Email successfully sent! Check your inbox.", vbOKOnly Or vbInformation

Double click and wait for instructions.

See also https://stackoverflow.com/search?q=is%3Aquestion+%5Bemail%5D+inline+image


After lot trials and failures I have found reason why Gmail does not show inline pictures. Originally I used this html:

"Some HTML text and an image \<img src="cid:image1"\>Nifty!"

and attached picture with this command (in python, but details are not important):

msg.add_related(img.read(), 'image', 'png', cid='image1')

It worked in most email clients, but not in Gmail android app and Gmail web. When I finally changed it to:

 msg.add_related(img.read(), 'image', 'png', cid='\<image1\>')

it is working in Outlook, Android Email, Android Gmail and Gmail web. This mistake is repeated in lots of examples of python libraries.


tl;dr

Gmail and certain other clients don't like base64 encoded images.

Full Story

The very first thing I did was view "Show Original" in Gmail. To my surprise, raw content still has your embedded image data:

enter image description here

That tells me right away that gmail is simply choosing to filter this content out. I was not able to find the reason. Some guesses point out the length of encoded data itself. Others talk about the general way in which Gmail filters out images. There are even records of this technique functioning a number of years back.

In addition, when viewing the same exact email in a third party client such as Newton(formerly Cloud Magic), I do see images properly rendered.

enter image description here

All of that points to a simple, however sad, fact that gmail doesn't like inline encoded images. Not in the browser and not in their mobile apps.

In fact, at the very end I did discover a post from 2013 by Campaign Monitor blog that concludes with the same results.

Don't use inline embedded images.


Are you using nodemailer?

If yes, there is the solution!

NPM has a package called nodemailer-plugin-inline-base64 that allow you to send an image with data uri to all emails providers like gmail, outlook, an others.

Setup:

yarn add nodemailer nodemailer-plugin-inline-base64

And try it out

var nodemailer = require('nodemailer');
var inlineBase64 = require('nodemailer-plugin-inline-base64');
transporter.use('compile', inlineBase64({cidPrefix: 'somePrefix_'}));
transporter.sendMail({
    from: '[email protected]',
    to: '[email protected]',
    html: '&lt;img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAAACCAYAAACE7KJkAAAAI0lEQVRYR+3DMQ0AAAgDsKlFzZxgEhOcbdIEAIBf7Y6qqn8P0MMQZPno7TMAAAAASUVORK5CYII=">'
});

Have a nice day!