I’ve been working on some custom html email templates. I’m having some trouble with my emails appearing differently when they are sent by different email services. I’m using AWS SES to send these emails to clients.
I’ve been using Postdrop to send test emails while I’ve been creating the template. Now that the AWS SES environment has been set up for my application, I can now send emails using the html template. The problem I’m getting is that when I send emails using AWS, the emails look different than they do when sent by Postdrop, even when viewing the email from the same email client.
I used a code checker to see how what the email client was receiving was different for each email sender, and they seem almost exactly the same, except for some Unicode(I think it’s Unicode?) differences. The only differences I noted was that SES adds =E2=80=8B
between certain sections of code, while Postdrop adds an empty line in place of that, and a short section of code where Postdrop included =2E
at the start of some of the classes, where SES did not(This is the "External Class" section shown in the picture.) It seems like these are related to tab or end-line characters, but I’m not sure. There is other Unicode code used as well, but both emails use the same code in those sections.
Here is an example of the input code:
And here is what I’m getting from the different email senders:
These slight differences are causing the email sent by SES to appear incorrectly. Specifically, some of the media queries aren’t working(while some still do), and there is additional spacing at the top.
In the header of the emails, both say they are being encoded by UTF-8, although the way it’s phrased is slightly different.
Postdrop appears like this:
Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: quoted-printable
and SES appears like this:
Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
Not sure if that would affect anything. What is causing this issue, and how do I fix it?
Edit
Here is the code for the Lambda function that tells SES to send the email, but some of the identifying stuff(specific email addresses, amazon region, and file structure) have been removed just for safety:
import boto3
import logging
import os
import sys
from emailbody import body
from emailbodytext import bodytext
from botocore.exceptions import ClientError
def send_email(recipients):
SENDER = "" # must be verified in AWS SES Email
RECIPIENTS = recipients # must be verified in AWS SES Email
AWS_REGION = ""
# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)
# Try to send the email.
try:
#Provide the contents of the email.
response = client.send_templated_email(
Destination={
'ToAddresses': RECIPIENTS,
},
Template='TemplateName',
TemplateData = "{}",
Source=SENDER
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
def delete_email_template(templateToDelete):
AWS_REGION = ""
# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)
# Try to get the email template.
try:
#Provide the contents of the email.
response = client.delete_template(
TemplateName=templateToDelete
)
return response
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print(response['MessageId'])
def create_email_template():
AWS_REGION = ""
#The name for the template
TEMPLATENAME = "Test"
# The subject line for the email.
SUBJECT = "Test Email"
#The email body
EMAILBODY = body
#The email text
BODYTEXT = bodytext
# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)
#Provide the contents of the email.
response = client.create_template(
Template={
'TemplateName': TEMPLATENAME,
'SubjectPart': SUBJECT,
'TextPart': BODYTEXT,
'HtmlPart': EMAILBODY
}
)
def lambda_handler(event, context):
emails = ['', '']
create_email_template()
send_email(emails)
delete_email_template("Test")
3
Answers
I found the answer to the problem today, and it's a silly one. The issue has nothing to do with the code or AWS. The non-ASCII characters that are being added are from Slack! Apparently if you copy html from Slack's code viewer directly, it adds additional characters for styling, which also happen to be non-ASCII characters. That explains why the emails were showing up incorrectly, as well as why it appeared that it was an encoding issue.
Thank you to everyone for all the additional contextual information.
I'm sure I'm not the only one that this Slack issue has affected, so I will let Slack know about this bug.
UPDATE: I contacted Slack and they already have this on the list of bugs to squash so hopefully a fix will come soon.
What you see is the
quoted-printable
encoding of your email. This encoding will convert any non allowed character to a=..=...
hex sequence. As described here. This encoding gets then parsed by the final mail client (or probably by a server in case of web mail like Gmail) and displayed to the end reader. As discussed here this parsing process is not very reliable.=E2=80=8B
is actually the encoding of a zero width space. So you have junk (invisible spaces) inside your email. Postdrop is probably just stripping them where SES not.Regarding the solution I would advise against non ASCII characters in your email, or at least inside the CSS portion of it as if would make the parsing very unpredictable. Otherwise you may try with other
Content-Transfer-Encoding
s.You need to minify that HTML email template. There are some tools for this. Even though both are sending your email, AWS is just messing it up in the process of trying to make adjustments…. Emails are very sensitive to charecter encoding. Most HTML template creating tools have a dev testing and then a build function that minifies and a bunch of other changes to the way CSS is written, even some make it all inline. That is not a valid HTML/CSS template for email.
Also not sure UTF-8 is what you want:
Encoding (string) —