Deliverability

Do you need a plain-text version of your newsletter?

TL;DR

Not for the spam score. In our test, a single-part HTML newsletter scored 0.1 points in SpamAssassin. A one-line "view in browser" stub as the text part scored 2.2, twenty-two times more, and a multipart message with the text part missing scored 2.3. If you send a plain-text version, make it a real copy of the issue, because tools that read text/plain first, ours included, take it at face value.

If you're asking whether you need a plain-text version of your newsletter, you've probably been told that leaving it out is a top spam trigger. Our own glossary and stats pages say it too. So on October 20, 2026 we tested it: one newsletter, seven MIME structures, a stock SpamAssassin install. The advice is backwards. Leaving the text part out costs almost nothing. A bad text part is what costs points.

Do you need a plain-text version? The short answer

You don't need one to pass a spam filter. If you send one, it has to say the same thing as the HTML, because a mismatched text part is the expensive mistake. And the reason to bother is not SpamAssassin at all. It is everything downstream that reads text/plain before it reads your HTML.

A quick vocabulary note. An email with both versions is a multipart/alternative message: one envelope, a text/plain part and a text/html part, and the mail client picks one to show. Most clients show the HTML. A spam filter looks at both.

What we tested, and what SpamAssassin scored

We wrote one realistic issue (about 370 visible words, six headings, seven links, a chart image with alt text, a style block, a hidden preheader and a footer with an unsubscribe link) and built it seven ways. Each file went through SpamAssassin 4.0.0 from the Ubuntu 24.04 package, with rules pulled by sa-update the same day, local tests only and Bayes switched off. In SpamAssassin's terms that is score set 0, where these MIME rules carry their highest weights.

Variant Rules that scored Total
A. Single part, text/html only MIME_HTML_ONLY 0.1 0.1
B. multipart/alternative, HTML part only MPART_ALT_DIFF 2.2, MIME_HTML_ONLY 0.1 2.3
C. Text part is a 7-word "View this email in your browser" stub MPART_ALT_DIFF 2.2 2.2
D. Text part is a full copy (388 words, links as URLs) none 0.0
E. Text part made by a naive tag stripper, CSS and all none 0.0
F. Text part is a 42-word teaser reusing the intro none 0.0
G. Image-heavy HTML (one linked image, two links) with the full text part MPART_ALT_DIFF_COUNT 2.8, HTML_SHORT_LINK_IMG_1 2.2, HTML_IMAGE_ONLY_04 1.7 6.7

Every variant also hit HTML_MESSAGE at 0.0 and two informational rules worth nothing, because we fed files straight in rather than relaying them over SMTP. The default threshold for spam is 5.0.

Two rows deserve a second look. Variant B is what you get when an ESP or a homegrown sender wraps the HTML in multipart/alternative and never attaches a text part. It scored worse than plain HTML, because the comparison runs and finds nothing to compare. Variant E is the one that should worry you. Its text part was garbage, and SpamAssassin gave it a perfect score.

How SpamAssassin decides your text part is different

The logic lives in the BodyEval plugin, in a function called _multipart_alternative_difference. It renders both parts to text, splits them on whitespace and keeps every token that contains a letter or digit. Then it crosses off each distinct HTML token that appears at least as often in the text part. If more than 99% of the HTML's distinct tokens survive, MPART_ALT_DIFF fires.

That threshold is loose. Our 42-word teaser shared one paragraph with the HTML and passed cleanly. The stub shared a handful of words at most, and failed. A multipart/alternative message with no text part has nothing to cross off, so it scores a 100% difference. A single-part HTML message never gets compared at all, which is why variant A got away with 0.1.

MPART_ALT_DIFF_COUNT is the reverse case. It fires when the text part has more than three times as many distinct tokens as the HTML. That is the classic image-only promo: a big linked banner in the HTML and the real copy hiding in the text part. Variant G crossed 5.0 with help from two image rules.

The points change with the filter's setup. Score set 0 has no Bayes and no network tests, set 1 adds network tests, set 2 adds Bayes, and set 3 has both. From rules/50_scores.cf in the Apache SpamAssassin source:

Rule Set 0 Set 1 Set 2 Set 3
MIME_HTML_ONLY 0.1 0.1 0.1 0.1
MIME_HTML_MOSTLY 0.1 0.1 0.1 0.1
MPART_ALT_DIFF 2.246 0.724 0.595 0.790
MPART_ALT_DIFF_COUNT 2.799 1.483 1.199 1.112

So the honest range for a stub is 0.6 to 2.2 points. On its own that won't send anyone to spam. But points stack, as we cover in how spam filters score your newsletter, and starting an issue at 2.2 means you've used 44% of the default threshold before the filter reads a word of your copy.

One caveat we can't get around. SpamAssassin publishes its weights, and so does Rspamd, the other big open-source filter, which we didn't test. SpamAssassin is what cPanel's spam filters run. Gmail, Outlook and Yahoo publish nothing comparable, so nobody outside those companies can tell you their number.

What a parser sees when there is no text part

Here is where the text part earns its keep. When Newsletrix collects a newsletter, our IMAP fetcher keeps the text/plain part if one exists. If it doesn't, we rebuild text from the HTML with a small function called html_to_text, which keeps text nodes and collapses whitespace. We ran it on a short sample issue:

<style>.btn{background:#ff6600;color:#fff}
  @media (max-width:600px){.col{width:100%}}</style>
<div style="display:none">Preheader: this week in growth</div>
<h1>Weekly Growth Notes</h1>
<p>Three things we learned.</p>
<p>Read the <a href="https://example.com/post?utm_source=nl">full case study</a>.</p>
<img src="chart.png" alt="Open rate rose from 31% to 38%">
<table><tr><td>Item one</td><td>Item two</td></tr></table>
<a href="https://example.com/unsub">Unsubscribe</a>
<script>var x=1;</script>

What came out, wrapped here for width (the real output is a single line):

.btn{background:#ff6600;color:#fff} @media (max-width:600px){.col{width:100%}}
Preheader: this week in growth Weekly Growth Notes Three things we learned.
Read the full case study . Item one Item two Unsubscribe var x=1;

The CSS came through first, as words. The script body came through too. The hidden preheader became body copy. Both URLs disappeared, so "full case study" points nowhere. And the alt text, the only place the 31% to 38% number lived, vanished completely, which is the same failure we describe in alt text in newsletters, just for machines instead of people. Any parser built like ours does the same. Your sentences arrive in one undifferentiated line.

The opposite problem is worse, and we found it in our own pipeline while writing this. When a text part exists, our readability features trust it. For the stub variant, Newsletrix scored the issue at Flesch-Kincaid grade 1.6 with a reading ease of 93.6, measured on the stub alone. The real newsletter reads at grade 6.0. A size check before trusting text/plain would catch this, and ours doesn't have one yet. If you compare reading levels across senders in any tool, ours included, a suspiciously low grade on one sender is worth a look at the raw message. How the analysis works explains what Newsletrix receives and which numbers come out of it.

Score the HTML side before you send

The free spam score checker reads your pasted HTML for content triggers: image-to-text ratio, hidden text, link hygiene, subject line risk. Pasted HTML has no MIME structure, so it cannot see your text part. Check that with Show original, below.

Run the spam score checker →

Who writes the text part for you

Mailchimp is clear about it. Its help article "Create a Plain-Text Email" says: "We automatically create and send a plain-text version of your HTML email to recipients who prefer to receive them." We couldn't find an equivalent statement in beehiiv's or Kit's help centres, so we won't guess about them or anyone else. Check a real send instead of a settings page.

Auto-generation is fine, with one condition: someone reads the output once per template. A generator is only as good as the HTML it starts from. Variant E shows the filter won't flag a bad conversion, and no subscriber is going to email you about it either.

Our own code has the variant B trap in it. Newsletrix's notification mail is built as multipart/alternative, and the text part is attached only if the calling code passes one. The function's docstring promised the text "falls back to HTML". It doesn't. Every caller passes a text body today, so nothing we send is HTML-only, but one forgotten argument would put our own mail at 2.3 points. If you run a homegrown sender, grep for the same pattern.

How to check whether your newsletter has a plain-text version

It takes two minutes. Send the issue to a Gmail address, open it, click the three-dot menu next to Reply and choose Show original. Look at the first Content-Type line in the headers. If it says text/html, you're variant A. If it says multipart/alternative, scroll down and find each part's own Content-Type. No text/plain part means variant B. If there is one, read it.

Reading it is the step people skip. If the part says Content-Transfer-Encoding: base64, Show original won't decode it for you. Thunderbird will. Choose View, then Message Body As, then Plain Text, and it shows the text part decoded when there is one. Apple Mail shows the raw source under View, Message, Raw Source. Our guide to Gmail's mailed-by and signed-by lines walks through the rest of what Show original tells you.

If you write or fix the text part yourself, this is what we put in ours:

  • The same words as the HTML, in the same order, with headings on their own lines.
  • Each link as "label: full URL", because the text part has no anchors.
  • Image alt text as a bracketed caption, so numbers in charts survive.
  • The full footer: postal address and unsubscribe URL.
  • No CSS, no tracking-pixel markup, no hidden preheader text.

A merge tag typo in a hand-edited text part is easy to miss, because almost nobody previews that part. Run the tags through the personalisation validator before the send. The cost is real: two versions means two things to QA every issue, and a team without the time should let the ESP generate the text part and check it once per template change.

Rendering QA across clients is a different job, the one Litmus is built for, and our Litmus comparison covers where competitor tracking fits instead. If you're working through deliverability more broadly, the newsletter audit checklist puts the text part next to authentication from SPF, DKIM and DMARC.

Our position, after the test: send a real text part or none. Never a stub.

Frequently asked questions

Does a missing plain-text version hurt deliverability?

In SpamAssassin, barely. A single-part HTML newsletter triggers MIME_HTML_ONLY, worth 0.1 points against a default threshold of 5.0. The bigger penalty is MPART_ALT_DIFF, worth 0.6 to 2.2 points depending on the score set, which fires when a multipart/alternative message has a text part that is missing or does not match the HTML. Gmail, Outlook and Yahoo do not publish their weights.

What is MIME_HTML_ONLY in SpamAssassin?

MIME_HTML_ONLY is a SpamAssassin rule described as "Message only has text/html MIME parts". It scores 0.1 points in all four score sets of the current ruleset. In our October 20, 2026 test it was the only scoring rule on a single-part HTML newsletter.

What triggers MPART_ALT_DIFF?

SpamAssassin compares the words in the HTML and text parts of a multipart/alternative message. If more than 99% of the distinct words in the HTML are missing from the text part, MPART_ALT_DIFF fires. A one-line "view in browser" stub triggers it, and so does a multipart/alternative message with no text part at all.

Does Mailchimp create a plain-text version automatically?

Yes. Mailchimp's help article "Create a Plain-Text Email" says: "We automatically create and send a plain-text version of your HTML email to recipients who prefer to receive them." The generated copy is only as clean as the HTML it is built from, so read one test send.

How do I check if my newsletter has a plain-text version?

Send the issue to a Gmail address, open it, and choose Show original from the three-dot menu next to Reply. If the top Content-Type is multipart/alternative and one of the parts below it is text/plain, you have a plain-text version. If the only body part is text/html, you do not.

Related reading

Get started

Stop guessing. Start winning.

Join newsletter creators using AI-powered competitor intelligence to ship better content, faster.

No credit card required  ·  Cancel anytime  ·  All features on every plan