emailmarketing.net

MIME & Transfer Encodings (RFC 2045/2046)

Deliverability-focused MIME digest: structure and headers, multipart/alternative ordering, quoted-printable and base64 rules and pitfalls, size inflation, and the malformed-MIME defects that trigger filtering.

Foundationalesp-operatorsender

MIME extends the ASCII-only, line-oriented RFC 5322 message format to carry multiple parts, non-ASCII text, and binary data over transports that only guarantee 7-bit lines. Every HTML campaign an ESP generates is a MIME message; encoding and structure defects are a recurring cause of mangled rendering and filter penalties. RFC 2045 defines the framework and encodings; RFC 2046 defines the media types.

The five MIME header fields (RFC 2045)

Field Requirement Notes
MIME-Version: 1.0 MUST be present, verbatim, on every MIME message Missing it means receivers may treat the message as pre-MIME plain text
Content-Type: type/subtype; parameter=value Subtype mandatory; type/subtype/parameter names case-insensitive, parameter values case-sensitive unless defined otherwise Default when absent: text/plain; charset=us-ascii
Content-Transfer-Encoding: mechanism One of 7bit (default) / 8bit / binary / quoted-printable / base64 / extension See below
Content-ID: <msg-id> Optional; MUST be world-unique Used to reference inline images (cid:) from HTML parts
Content-Description Optional, US-ASCII (RFC 2047 for non-ASCII) Rarely load-bearing

Content-Transfer-Encoding mechanisms

Mechanism Constraint on the data Actually transforms?
7bit Lines ≤ 998 octets between CRLFs; no octets > 127; no NULs No — a declaration that data already conforms
8bit Lines ≤ 998 octets; octets > 127 allowed; no NULs; CR/LF only as CRLF pairs No — requires the 8BITMIME SMTP extension end-to-end
binary Any octet sequence, no line-length limit No — requires BINARYMIME/CHUNKING; essentially unused in Internet mail
quoted-printable Output is 7-bit, lines ≤ 76 chars Yes
base64 Output is 7-bit, lines ≤ 76 chars Yes

Composite-type restriction: it is "EXPRESSLY FORBIDDEN" to use any encoding other than 7bit, 8bit, or binary on multipart/* or message/* entities — encoding applies to leaf parts only, never to a container (prevents nested encode/decode passes). (RFC 6532 later relaxes this for message/global only — see SMTPUTF8/EAI.)

Quoted-printable rules (RFC 2045 §6.7)

Designed for data that is mostly ASCII text — encoded output stays largely human-readable.

  1. Any octet may be represented as =XX with uppercase hex digits (lowercase is not permitted on generation, though robust decoders accept it).
  2. Octets 33–60 and 62–126 (printable ASCII except =) may appear literally.
  3. TAB (9) and SPACE (32) may appear literally but MUST NOT be literal at the end of an encoded line — trailing whitespace must be =09/=20 (transport may strip trailing spaces silently).
  4. A hard line break in text is represented as a real CRLF.
  5. Encoded lines MUST be ≤ 76 characters (not counting trailing CRLF). Longer content lines use a soft line break: = as the last character, which the decoder deletes along with the following CRLF.
  6. When encoding binary data in QP, CR and LF must be encoded as =0D/=0A (a binary CRLF becomes =0D=0A) — QP for binary is legal but base64 is the right tool.

Practical: QP inflates typical English/Western-European HTML only slightly (3 bytes per encoded octet); for scripts where nearly every character is multi-byte UTF-8 (CJK, Cyrillic), QP approaches 3× inflation and base64 (~1.37×) is smaller.

Base64 rules (RFC 2045 §6.8)

  • 65-character alphabet: A–Z (0–25), a–z (26–51), 0–9 (52–61), + (62), / (63), = padding.
  • 3 input octets → 4 output characters; final partial group padded: 1 octet → 2 chars + ==, 2 octets → 3 chars + =. Any = marks end of data.
  • Encoded output MUST be in lines of ≤ 76 characters.
  • Text line breaks must be converted to CRLF before encoding (canonical form), so a decoded text part is CRLF-delimited regardless of the composing platform.
  • Size inflation: 4/3 (~33%) plus CRLFs every 76 chars ≈ 37% total. Provider message-size limits (e.g., Gmail's 25 MB) apply to the encoded message — a "20 MB attachment" is ~27 MB on the wire.

Choosing an encoding (sender guidance)

  • HTML/plain-text parts in UTF-8: quoted-printable is the safe universal default — it survives 7-bit hops, keeps diffs/DKIM canonicalization predictable, and avoids depending on 8BITMIME negotiation. 8bit is fine when your MTA verifies 8BITMIME support on every hop (essentially universal today, but a 7-bit-only hop must then re-encode, which breaks DKIM signatures — sign after final encoding, or use QP so no hop ever re-encodes).
  • Attachments and images: base64, always.
  • Never emit lines over 998 octets in any part (long unwrapped HTML lines are a classic generator bug — QP's 76-char soft-wrapping fixes this for free).

Media types (RFC 2046)

Discrete types: text, image, audio, video, application. Composite: multipart, message. Unrecognized subtypes fall back: text with known charset → text/plain; other discrete → application/octet-stream; unknown multipart/* → treated as multipart/mixed.

Multipart syntax

  • The boundary parameter is mandatory: 1–70 characters, must not end in a space; allowed characters are DIGIT/ALPHA and '()+_,-./:=? (and space, not last). Because /, =, ?, : and space can appear, the boundary value usually needs quoting in the Content-Type header.
  • The delimiter is a line starting --boundary; the closing delimiter is --boundary--. The boundary must not appear in any encapsulated part — generators should use high-entropy boundaries rather than checking.
  • Each body part = optional headers + blank line + body. Parts with no Content-Type default to text/plain; charset=US-ASCII (except inside multipart/digest, where the default is message/rfc822).
  • Preamble (before first boundary) and epilogue (after closing delimiter) are ignored by MIME readers; the preamble is occasionally used for "this is a MIME message" notes to legacy clients.

Multipart subtypes that matter for senders

Subtype Semantics Deliverability notes
multipart/mixed Independent parts, in order Message + attachments
multipart/alternative Same information, alternative renditions. Parts MUST be in increasing order of preference/faithfulness — the best/richest version LAST. Clients display "the last part of a type supported by the recipient system's local environment." The classic structure is text/plain first, text/html last. Reversed order makes many clients show the plain-text part; a well-formed pair (with the plain part actually matching the HTML content) is a long-standing spam-filter positive signal, while an empty or boilerplate plain part is a mild negative
multipart/related (RFC 2387, referenced here for completeness) HTML + inline cid: images Standard structure for embedded images
multipart/digest Parts default to message/rfc822 Rare in ESP traffic
multipart/parallel Order not significant Effectively unused
multipart/report (RFC 6522) Machine-readable reports The container for DSNs and FBL ARF reports

Canonical campaign nesting: multipart/mixed( multipart/related( multipart/alternative( text/plain, text/html ), inline images ), attachments ) — collapse levels you don't need.

message/* subtypes

  • message/rfc822: a full encapsulated message (used for forwards, FBL reports, returned originals in bounces). Encoding restricted to 7bit/8bit/binary.
  • message/partial: fragmented delivery with reassembly (parameters id, number starting at 1, total on the last piece; 7bit only) — obsolete in practice, and filters treat fragmentation as an evasion technique. Never use.
  • message/external-body: body by reference (access-types FTP/ANON-FTP/TFTP/LOCAL-FILE/MAIL-SERVER) — also effectively dead and filter-suspicious.

text/plain and charsets

Default charset is US-ASCII (meaning ANSI X3.4-1986 specifically). Always declare charset= explicitly; label content with the lowest common denominator that actually covers it. In modern practice: just use charset=utf-8 everywhere and be done.

Malformed-MIME defects that trigger filtering

Spam filters score MIME conformance because sloppy generators correlate with spamware (SpamAssassin's MIME_* rule family and equivalents in commercial filters test exactly these). Defects worth linting for in an ESP's message builder:

Defect RFC rule violated
Missing MIME-Version: 1.0 on a message with MIME structure 2045 §4 MUST
Missing or unmatched final --boundary-- closing delimiter 2046 §5.1.1
Boundary string occurring inside a part's body 2046 §5.1.1 "must not appear inside any of the encapsulated parts"
Raw 8-bit bytes in a part declared 7bit/quoted-printable, or in headers 2045 §2.7/6.7
Lines > 998 octets (unwrapped HTML) 2045 §2.7 / 5321 limit
QP lines > 76 chars, lowercase =xx hex, literal trailing whitespace 2045 §6.7 rules 3 & 5
Base64 with invalid characters or broken padding 2045 §6.8
Content-Transfer-Encoding other than 7bit/8bit/binary on a multipart 2045 §6.4
Charset declared but content not valid in it (e.g., us-ascii label on 8-bit body) 2045 §5.2 / 2046 §4.1
multipart/alternative with HTML before plain text, or HTML-only inside an alternative 2046 §5.1.4 ordering
Bare CR or LF (not CRLF pairs) in transmitted data 2045 §2.8 / 5321
message/partial fragmentation filter-evasion heuristic

None of these alone typically blocks delivery at major providers, but they stack with reputation, and several (boundary bugs, encoding mismatches) also cause visible rendering corruption — which drives "report spam" clicks.

See also

#rfc#mime#encoding#quoted-printable#base64#multipart#content-type#message-format