MIME: How Text, HTML and Attachments Fit Into One Message

« Blog · By

By its original design an email is a plain text file: headers, a blank line, text. Seven bits per character, no accents, no attachments, no formatting. That an invoice as PDF, a logo inside the text and two versions of the same letter can sit in one message today is owed to a retrofitted extension: MIME.

MIME changes nothing about the basic principle. A message is still a text file. Everything else is rewritten as text and filed into a tree structure described in headers.

The tree of an ordinary business email

A message with HTML text, an embedded logo and a PDF attachment looks like this on the inside:

multipart/mixed ├ multipart/related │  ├ multipart/alternative │  │  ├ text/plain │  │  └ text/html │  └ image/png └ application/pdf frame: content plus attachments content plus embedded images the plain-text version the HTML version the logo, addressed as cid:logo the actual attachment two versions of the same text What the recipient sees of this: a text, an image and one attachment.
Three nested frames. Each has its own job — and the order of nesting is not a matter of taste.

The three frames and what they are for

TypeMeaningWhat the client does with it
multipart/mixedSeveral parts, all equaldisplays them one after another
multipart/alternativeThe same content in several formsdisplays one of them — the last it can render
multipart/relatedOne main part plus files belonging to itdisplays the main part and pulls the rest as needed

The middle row is where the ordering matters. In alternative the rule is: the part listed last takes precedence. That is why the text version always comes before the HTML version and never after it. Swap them and clients capable of both will show the plain text.

How the parts are separated

Since everything is text, a separator is needed. It sits in the frame's header:

Content-Type: multipart/alternative; boundary="=_a7f31c9b4e2d"

--=_a7f31c9b4e2d
Content-Type: text/plain; charset=UTF-8

Good afternoon, your invoice is attached.

--=_a7f31c9b4e2d
Content-Type: text/html; charset=UTF-8

<p>Good afternoon, your invoice is attached.</p>

--=_a7f31c9b4e2d--

The boundary has to be random enough not to occur in the content — otherwise the message falls apart in the wrong place. The closing boundary carries two extra hyphens at the end; without them, some clients consider the message truncated.

Why attachments grow larger than they are

A PDF consists of arbitrary bytes, but the message body may only contain text. So it gets re-encoded, usually with Base64: every three bytes become four characters.

That produces an overhead of almost exactly one third, plus line breaks. A 15 MB file occupies around 20 MB in the message.

This explains a common surprise: a mailbox with a 25 MB limit does not accept a 25 MB file but around 18 MB. The limit applies to the finished message, not to the file inside it.

For text the more economical quoted-printable is normally used: it leaves ordinary characters alone and rewrites only the rest. That is where the =C3=A4 sequences visible in raw messages come from — that is an accented character, not an error.

Embedded image or attachment?

Technically both are the same thing: a file inside a part of the message. The difference lies in two headers.

An embedded image carries an identifier and is addressed from the HTML above it:

Content-Type: image/png
Content-ID: <logo>
Content-Disposition: inline

… in the HTML part: <img src="cid:logo">

An attachment carries Content-Disposition: attachment and a filename instead. Except: that field is a wish, not an instruction. Some clients list every file in the attachment bar, the embedded one included. Which is why recipients of a message with a logo in the header area sometimes see "2 attachments" when only one was meant.

What follows in practice

  • Always send a real plain-text version. Not as a box-ticking exercise: it is the version that arrives when HTML cannot be rendered — and a text part that is empty or filled with "this message requires HTML" stands out when the message is being judged.
  • Mind the order in alternative. Text first, HTML second.
  • Link large files rather than attaching them. The one-third overhead hits every intermediate hop, and each has a limit of its own.
  • Set the character set per part. It is not global but sits in every Content-Type — a missing or wrong value is the most common cause of unreadable special characters.

Looking inside for yourself

Every mail client can display a message's source. The whole tree lies open in it: the boundaries, the types, the encodings. Anyone who has once looked up why a particular attachment has a different name at the recipient's end, or why an image does not appear, usually finds the answer there within a few lines — it is a text file, and it is built so a human can read it.