An HTML-only email displays raw markup in Gmail's push notification preview, in plain-text email clients used in corporate environments, and in assistive technologies that cannot parse HTML. Spam filters from Barracuda, SpamAssassin, and Proofpoint penalize the absence of a multipart/alternative structure because it is a known characteristic of automated spam. RFC-2046 defines the multipart/alternative MIME structure specifically to ensure that all receivers can display a meaningful representation of the message.
Info because the consequence is display degradation and minor spam score penalties rather than security exposure or data loss.
Auto-generate the plain-text part from the rendered HTML using html-to-text in the send path:
import { convert } from 'html-to-text'
export function prepareEmailParts(html: string, explicitText?: string) {
const text = explicitText ?? convert(html, {
wordwrap: 80,
selectors: [
{ selector: 'a', format: 'inline', options: { hideLinkHrefIfSameAsText: true } },
{ selector: 'img', format: 'skip' }
]
})
return { html, text }
}
Pass both html and text to every ESP send call. The text field must not be an empty string — that is treated identically to an omitted field by spam filters.
ID: sending-pipeline-infrastructure.template-engine.plain-text-alternative
Severity: info
What to look for: Check whether outgoing HTML emails include a plain-text alternative part (multipart/alternative MIME structure). Look at how the email is assembled before it is passed to the ESP SDK. A missing plain-text part causes Gmail and other clients to display raw HTML in notifications and previews, and some spam filters penalize missing plain-text alternatives.
Pass criteria: All HTML emails include a plain-text alternative. Count all ESP send call sites — 100% must set both html and text fields. The plain-text body is either automatically generated from the HTML (using html-to-text or equivalent) or maintained as a separate template. Is NOT a pass when the text field is set to an empty string.
Fail criteria: HTML emails are sent without a plain-text alternative. The ESP call includes html but the text field is omitted or empty.
Skip (N/A) when: The application exclusively sends plain-text emails (no HTML body) — confirmed by the absence of HTML templates.
Detail on fail: "ESP send call sets html body but omits text field — no plain-text alternative in MIME structure" or "text field set to empty string for all sends"
Remediation: Auto-generate plain-text from the rendered HTML:
import { convert } from 'html-to-text'
async function prepareEmailParts(html: string, explicitText?: string) {
const text = explicitText ?? convert(html, {
wordwrap: 80,
selectors: [
{ selector: 'a', format: 'inline', options: { hideLinkHrefIfSameAsText: true } },
{ selector: 'img', format: 'skip' }
]
})
return { html, text }
}
// In the send path:
const { html, text } = await prepareEmailParts(renderedHtml)
await esp.send({ to, subject, html, text })