leastfixedpoint

HTML Email is hard to get right

This page is a mirrored copy of an article originally posted on the (now sadly defunct) LShift blog; see the archive index here.

Tue, 18 July 2006

For a recent project, we developed support for sending automatically-generated HTML emails. Now, most people do this by including a message body with MIME-typetext/html. For extra points, sometimes there’s also a text/plain part alongside the HTML in a multipart/alternative container.

The problem with doing things this way is that you can’t include any images or other resources (such as CSS) as separate parts of the email linked to from the main HTML body-part. For that, you need to use the multipart/related MIME-type. Unfortunately, few commonly-used email clients render multipart/related HTML-plus-resource aggregations well.

We only tried the arrangement where the multipart/related, containing the main HTML page and its associated resources, was a sibling of the text/plain part within the multipart/alternative container. The inverse arrangement, with the multipart/alternative as the main document within the multipart/related part, was something we have yet to experiment with.

Here’s a picture of the structure of our initial attempts:

multipart/alternative
 |
 +-- text/plain
 +-- multipart/related
      |
      +-- text/html
      +-- image/gif
      +-- text/css

This worked reasonably well in Thunderbird and Outlook 2002, but we had consistent reports from our customer that the images and stylesheet would randomly fail to display in Outlook 2003 (SP2). After lots of mucking around trying to get Outlook to either work reliably or fail reliably, we gave up on that line and instead simplified the structure of our emails, putting the CSS styling inline in the HTML HEAD element:

multipart/alternative
 |
 +-- text/plain
 +-- multipart/related
      |
      +-- text/html (with text/css inline in HEAD)
      +-- image/gif

This didn’t work particularly well, either: it seems many email clients ignore styles set in the HEAD element. Finally, we moved to applying CSS styling inline, using a style attribute on each styled element. We were able to use an XSLT transformation to allow us to write clean HTML and apply the CSS style attribute automatically. The final structure of the emails we sent:

multipart/alternative
 |
 +-- text/plain
 +-- multipart/related
      |
      +-- text/html (with text/css copied on to each element!)
      +-- image/gif

This seems to work more-or-less reliably across

If I was to do it all again, I’d give serious consideration to the traditional non-multipart text/html solution with images hosted by some public-facing web server. We managed to get our multipart-HTML-emails working acceptably, but only by the skin of our teeth.

References:

Comments

On 18 July, 2006 at 12:23 pm, Paul Crowley wrote:

I have my email client configured to block remote image loading for security reasons - it gives others a way to see when you’re reading their email. In particular, I don’t want spammers to be able to tune their subject lines to maximize the probability that I’ll read their email, which they can otherwise do using “web bugs”. So your last solution won’t reliably work either - sorry!

On 18 July, 2006 at 4:25 pm, mikeb wrote:

http://css-discuss.incutio.com/?page=StyleInEmail is the best summary I’ve seen of what email clients can and will do with HTML emails (and it’s recent).

It’s worth stating that our problem was mainly with making the emails work perfectly in one email client, rather than good-enough in all clients—in the latter case, this followup to an AListApart article http://www.campaignmonitor.com/blog/archives/2005/08/optimizingcss1.html is a handy reference also.

On 3 July, 2008 at 5:46 pm, tonyg wrote:

((I accidentally deleted a bunch of comments I didn’t mean to delete today, so I’m having to repost them manually:))

Matthew Sherborne wrote:

I’ve just been through the same exercise. Could I get a copy of your XSLT sheet please :)

On 3 July, 2008 at 5:46 pm, tonyg wrote:

((I accidentally deleted a bunch of comments I didn’t mean to delete today, so I’m having to repost them manually:))

mikeb wrote:

@Matthew: the XSLT is from our domain model to a very simple MIME-container + HTML model; the actual assembly of the MIME message is done in Scheme. The XSLT really just lets us apply the style attribute consistently.

On 17 January, 2009 at 7:48 pm, josh wrote:

can you give me som more info on how you did the XSLT to move the CSS inline? We are looking for the same thing and would love a jump start