HTML email
HTML email is HTML rendered by dozens of clients that each remove what they dislike. There is no shared engine and no versioning: the same message is one layout in a desktop client, another on a phone, and plain text in a third. Writing it is an exercise in using the small subset everything agrees on.
The subset that survives#
| Construct | Typical treatment |
|---|---|
| Table-based layout | Renders nearly everywhere. Still the most reliable structure |
Inline style attributes | Widely honoured. The safest place to put styling |
<style> in the head | Often kept, sometimes stripped entirely — a message must be readable without it |
| External stylesheets | Not fetched. Treat as absent |
| Web fonts | Usually ignored. Always specify a real fallback stack |
| Background images | Inconsistent. Never carry meaning in one |
<script> | Removed by every serious client. Mail is not an application platform |
| Forms | Removed or neutered. A link to a page is the working pattern |
| Dark mode | Clients may invert colours themselves, sometimes over your palette |
The plain-text alternative is not a courtesy. It is what a text-only client shows, what several automated systems read, and what remains legible when the HTML is stripped by a security gateway. A message whose text part says "view this in an HTML client" has no content for those readers at all.
Reading it is a security problem, not a rendering one#
Composing HTML email is fiddly. Displaying HTML email from strangers is dangerous, and the two problems are usually discussed as though they were one. Incoming markup is untrusted input that arrives with an invitation to render it.
- Script and event handlers must be removed, not escaped and not disabled — removed.
- Remote resources are network requests to a sender-chosen server. Each one reports that the message was opened, from where, and on what.
- CSS can exfiltrate and obscure. Positioning can hide text from a reader while leaving it in the document; attribute selectors have been used to leak content.
- Link text can lie. A visible
https://example.netpointing elsewhere is the oldest trick in mail and still the most effective. - Embedded content and frames pull in whole documents from elsewhere.
- Prompt injection. Where an assistant reads the mail, instruction-shaped text inside a message is an attempt to give commands to the agent rather than information to the reader.
Does Connect use HTML email, and how is incoming HTML handled?#
Used on both paths, with the reading path deliberately distrustful. mail_render.py sanitises incoming HTML aggressively before any of it reaches a screen, and remote images are proxied rather than fetched by the reader's browser — so opening a message in Connect does not tell the sender that you opened it, or from where.
That proxying is a small feature with a disproportionate effect. Read receipts by tracking pixel are ubiquitous in commercial mail, and they work by measuring a fetch the reader's own browser makes. Making the fetch from elsewhere removes the timing and the address from the signal the sender receives.
On the sending side, a reply is composed and goes out through outbound.py, the one send boundary shared by the agent and by a person. A held draft is editable before it goes — the subject, the body and the recipients — and what is approved is what is sent. The mailbox's signature belongs to the mailbox rather than to the message, which is why it does not vary with who released the reply.
If you are composing#
- Write the plain-text part first and make it complete. If the message does not work as text, it does not work.
- Use a table for structure and inline styles for appearance; keep any
<style>block to enhancements you can lose. - Set explicit width and height on images and provide
alttext — many clients block images by default, andaltis then the whole message. - Make links absolute and obvious, and let the visible text match the destination.
- Keep the message narrow enough to read on a phone without zooming; that is where most mail is opened.
- Test by sending to accounts on the clients your recipients actually use. Preview tools approximate; clients decide.
Questions#
Why does the same email look different in two clients?
Because each client applies its own rules to your markup before rendering: one keeps the head stylesheet, another strips it; one honours a web font, another substitutes; one inverts colours for dark mode. There is no shared rendering engine and no conformance test, so consistency comes from using less, not from specifying more.
Should a business send HTML or plain text?
Send both, as a multipart/alternative, with a text part that stands on its own. For a one-to-one reply, restrained HTML that degrades to good text is the pattern that works everywhere — and for anything transactional, the text part is what several automated readers will actually process.
Does blocking remote images break messages?
It changes them: images the sender expected to appear do not. Proxying rather than blocking keeps the picture visible while removing the tracking signal, which is why it is the better default. Anything that carried meaning only in an image is a message that was already fragile.