MIME encoding email explained is one of those topics where most guides either stop at the Content-Type header or point you directly at RFC 2045 and wish you luck. Neither approach is useful when you're staring at a raw .eml file trying to understand why an attachment rendered as garbled ASCII or why a forwarded message shows up as a nested blob. This guide walks the full MIME tree, boundaries, encoding schemes, multipart nesting, and the command-line tools to pull it apart, the way a senior mail engineer would explain it on a whiteboard.

What MIME Actually Does, and Why Plain RFC 822 Wasn't Enough

The 7-bit ASCII wall and the problem it created

RFC 822, published in 1982, defined the format for internet mail messages. Its fundamental constraint: message bodies must be 7-bit US-ASCII text, with lines no longer than 1000 characters. That works fine for English prose. It fails completely the moment you need to send a JPEG, a Word document, a PDF, or even a plain-text message written in French, Arabic, or Japanese.

The SMTP transport layer compounded the problem. Many MTAs stripped the 8th bit of every byte as mail transited between systems, a practice that silently corrupts any binary data or multi-byte character sequence. There was no header field to declare encoding, no container to group a body with its attachments, and no standard way to tell a client what content type it was receiving.

By the late 1980s, workarounds like uuencoding had proliferated, but they were informal, client-dependent, and invisible to the protocol. The 7-bit wall was a structural constraint, not a configuration gap.

How MIME extended the envelope without breaking SMTP

MIME, Multipurpose Internet Mail Extensions, defined across RFC 2045–2049, solved this by layering structure on top of the existing message format rather than replacing it. SMTP and RFC 822 stayed untouched. MIME added a set of header fields that describe what the body contains and how it's encoded, letting any MIME-aware client interpret the content correctly while MIME-unaware systems still see a well-formed RFC 822 message (even if they can't render it usefully).

This backward-compatibility design is deliberate. Adding structure through headers rather than modifying the transport layer is why a message composed in 2026 can still be relayed by an MTA written decades earlier. The protocol seam is an engineering choice, not an accident.

Anatomy of a MIME Message: Headers That Define the Structure

MIME-Version and Content-Type: the mandatory pair

Every MIME message starts with this declaration:

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----=_Part_12345"

MIME-Version: 1.0 is the signal that this message uses MIME. The value has been 1.0 since the original specification; encountering any other value is a red flag.

Content-Type is where the real information lives. It carries two things: the media type (e.g. multipart/mixed, text/html, image/jpeg) and, for multipart messages, the boundary parameter. The boundary is a string chosen to not appear anywhere in the message body. It acts as the structural delimiter between parts. If the Content-Type header is absent, malformed, or lists the wrong boundary string, every parser downstream fails silently, parts don't split, attachments don't decode, and clients either show raw text or drop content entirely.

Two other header fields matter for every practitioner reading a raw message:

  • Content-Disposition, declares whether a part is inline (rendered in the message body) or an attachment (offered as a download), and carries the filename parameter.
  • Content-ID, provides a unique identifier for inline parts referenced by cid: URIs in HTML bodies, used in multipart/related messages.

Content-Transfer-Encoding: how body encoding is declared

The Content-Transfer-Encoding (CTE) header field tells the receiver how the part's body has been encoded for transit. Valid values are 7bit, 8bit, binary, quoted-printable, and base64. The first three are pass-through declarations (no transformation applied); the last two are actual encoding schemes covered in depth below.

A specimen from a real message part:

Content-Type: application/pdf; name="report.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="report.pdf"

This tells a parser: the content is a PDF, its bytes have been base64-encoded for transport, and it should be offered as a downloadable attachment named report.pdf.

Inside the MIME Tree: Multipart Types and Nested Boundaries

multipart/alternative: plain text and HTML as siblings

multipart/alternative groups two or more representations of the same content. The canonical use is pairing a plain-text fallback with an HTML version of the same message body:

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

------alt_boundary_001
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Hello, this is the plain text version.

------alt_boundary_001
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<html><body><p>Hello, this is the <b>HTML</b> version.</p></body></html>

------alt_boundary_001--

The client is expected to render the last part it can handle, so plain text comes first, HTML comes last. The closing delimiter (boundary--) signals the end of the multipart block.

multipart/mixed and multipart/related: attachments and inline content

multipart/mixed is the outer container for a message with attachments. multipart/alternative nests inside it, not the other way around. The nesting order matters: the alternative block is one sibling part, and the attachment is another:

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="outer_boundary_XYZ"

--outer_boundary_XYZ
Content-Type: multipart/alternative; boundary="inner_boundary_ABC"
  ← plain text part (text/plain)
  ← HTML part (text/html)
--inner_boundary_ABC--

--outer_boundary_XYZ
Content-Type: application/pdf; name="report.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="report.pdf"

[base64-encoded PDF data]

--outer_boundary_XYZ--

multipart/related is used when the HTML body references inline resources, embedded images, CSS, via cid: URIs. The HTML part and its resources are grouped together, and the client resolves the references internally before rendering.

Nested message/rfc822 parts and the recursive MIME tree

When a message is forwarded as an attachment (not inline text), the forwarded message travels as a message/rfc822 part. This is a complete RFC 822 message embedded inside a MIME part:

--outer_boundary_XYZ
Content-Type: message/rfc822
Content-Disposition: attachment

From: sender@example.com
To: recipient@example.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="forwarded_boundary"
...

A parser encountering message/rfc822 must recurse: parse the embedded message as a full MIME message, with its own headers, its own boundary, and potentially its own nested parts. The MIME tree is recursive by design. For reading the full delivery path in email headers, this recursion is also where forwarded message headers live, each embedded message carries its own Received chain.

Base64 and Quoted-Printable: How Email Body Encoding Actually Works

Base64 email encoding: the mechanics and the 33% overhead

Base64 encodes binary data as printable ASCII by converting every 3 bytes of input into 4 ASCII characters of output, a fixed 4:3 expansion ratio. A 1 MB PDF attachment becomes approximately 1.37 MB encoded. For large attachments, base64 email encoding is a non-trivial size cost; factor it in when designing systems that send bulk messages with attachments.

The encoded output is wrapped at 76 characters per line, per RFC 2045. A short binary input like the three bytes 0x4D 0x49 0x4D encodes to TUlN, each group of three bytes maps to a four-character block using the base64 alphabet (A–Z, a–z, 0–9, +, /), with = padding at the end if the input length isn't a multiple of three.

Use base64 for binary files (images, PDFs, archives) and any content where most bytes are non-ASCII.

Quoted-printable encoding: when most characters are already safe

Quoted-printable (QP) is the right choice when a body is mostly ASCII with occasional non-ASCII characters, a common case for Western European languages. Safe characters (printable ASCII except =) pass through unchanged. Non-ASCII bytes are encoded as =XX where XX is the uppercase hex value of the byte.

A French sentence encoded in quoted-printable:

R=C3=A9servation confirm=C3=A9e

The character é is U+00E9, encoded as UTF-8 bytes 0xC3 0xA9, which QP renders as =C3=A9. The rest of the sentence is unchanged. If a line would exceed 76 characters, a soft line break, a trailing = followed by a newline, signals that the line continues:

Voici une ligne tr=C3=A8s longue qui d=C3=A9passe la limite de soixante-seize=
 caract=C3=A8res.

The = at end-of-line is stripped by the decoder; the line is joined before decoding. Use quoted-printable for HTML and text bodies with sparse non-ASCII content. Applying base64 to a body that's 95% ASCII adds unnecessary bulk.

Decoding a MIME Email Manually: A Step-by-Step Walkthrough

Reading the MIME boundary and splitting parts

Start with the raw .eml file. The boundary value is in the top-level Content-Type header:

Content-Type: multipart/mixed; boundary="----=_Part_12345"

The boundary value is ----=_Part_12345. In the message body, each part begins with -- prepended to that string, and the final delimiter appends -- to the end as well:

------=_Part_12345
[part headers and body]
------=_Part_12345
[next part]
------=_Part_12345--

To split manually, search the raw file for every line starting with ------=_Part_12345. Each interval between delimiters is one MIME part. Read that part's own Content-Type and Content-Transfer-Encoding headers to know what you're dealing with. If a part's Content-Type is itself multipart/*, repeat the process with the new boundary, recurse until you hit leaf parts. This is where diagnosing delivery failures from raw headers and MIME parsing converge: both require reading the raw message structure before any client interpretation.

Decoding a base64 attachment at the command line

Once you've isolated the base64 block for an attachment part, extract it and decode it with standard Unix tools:

# Extract the base64 block between boundary delimiters
awk '/------=_Part_12345/{found++} found==2 && !/------/{print} found==3{exit}' message.eml \
  | grep -v "^Content-" \
  | grep -v "^$" > encoded.b64

# Decode to the original binary
base64 -d encoded.b64 > report.pdf

The awk command counts boundary occurrences to isolate the second part, skips the part's header lines (Content-*) and the blank separator line, and writes only the base64 data. base64 -d decodes it back to binary. On systems where base64 -d isn't available, openssl enc -base64 -d -in encoded.b64 -out report.pdf achieves the same result.

Verify the output: file report.pdf should confirm the correct file type. If it reports data or shows garbled bytes, the extraction likely included a boundary line or a header line, re-check the awk range.

Common MIME Failures and How to Diagnose Them

Missing or mismatched boundary parameters. If the boundary value in the Content-Type header doesn't exactly match the delimiters in the body, including case, hyphens, and whitespace, parsers either fail to split the message or treat the entire body as a single undivided block. The fix is mechanical: extract the declared boundary string, search the raw body for it, and confirm character-for-character alignment. Mismatches often originate from MTA rewriting or from mail composition libraries that generate the boundary after writing the body.

DKIM body-hash breaks from CTE normalization. DKIM signs the canonicalized message body. When a relay normalizes Content-Transfer-Encoding, re-encoding a quoted-printable part or adding trailing whitespace, the body hash computed at signing no longer matches the body hash at verification. The Authentication-Results header will show dkim=fail (body hash did not verify). DKIM signature verification failures of this type are diagnosed by comparing the signed body hash in the DKIM-Signature header (bh= tag) against the hash of the received body, if they diverge and the headers are intact, a transit relay modified the body. Understanding how SPF, DKIM, and DMARC interact helps distinguish a body-hash failure from an alignment failure. For deeper triage, interpreting the Authentication-Results header shows exactly which check failed and at which hop.

Client rendering the wrong multipart/alternative branch. Some clients, particularly older mobile clients and certain webmail renderers, render the first alternative part rather than the last. If your HTML version isn't displaying, confirm that text/plain precedes text/html in the multipart/alternative block. Also confirm that the Content-Type of the HTML part declares charset correctly; a missing or wrong charset causes mojibake even when the part is selected correctly.


This article covers the structural mechanics of MIME, boundaries, nesting, encoding schemes, and the recursive message/rfc822 container, at the level a practitioner needs to read raw messages without guessing. The full chapter on MIME encoding in the Email Decoded reference extends this into MIME security considerations, S/MIME structure, and the interaction between MIME structure and spam filter scoring, with additional annotated message specimens and a complete header-field reference.