# Invisible Unicode Characters in Email: How to Find the Ghosts in Your Templates

> Source: <https://dev.to/emaildigit/invisible-unicode-characters-in-email-how-to-find-the-ghosts-in-your-templates-32bb>
> Published: 2026-11-11 20:40:35+00:00

Picture a Tuesday deploy. Marketing pastes the new welcome email copy from a shared doc into your template, you ship it, and everything looks perfect in the preview. Two days later a support ticket arrives: a customer's email client showed one line of the message in a strange order, and your keyword-based content filter in staging never flagged the word it was built to catch. You diff the copy. The diff is empty. Nothing is wrong. Except something is.

Welcome to **invisible Unicode characters in email**: characters that take up a position in your string, draw nothing (or rearrange what's around them), and survive every copy-paste with the stealth of a ninja who also happens to be a lawyer.

Most text never contains these, which is exactly why they surprise people.

| Family | Examples | What it does | 
|---|---|---|
| Direction controls | Right-to-left override (U+202E), left-to-right embedding (U+202A), isolates (U+2066 to U+2069) | Tell the renderer to lay out the following characters in a different direction | 
| Zero-width characters | Zero-width space (U+200B), word joiner (U+2060) | Occupy a position in the string but render as nothing | 

Both have honest jobs in typesetting. The trouble starts when the text a reader sees and the text your code stores stop being the same string.

The classic demonstration is a file name. Put a right-to-left override in front of `gpj.exe` at the end of `invoice_`, and the name displays as `invoice_exe.jpg`. It looks like a picture. It's a program.

In 2021, security researchers showed the same trick making source code read one way to a reviewer and compile another way. That research, tracked as [CVE-2021-42574](https://nvd.nist.gov/vuln/detail/CVE-2021-42574), is why the technique is now widely called "Trojan Source".

Zero-width characters are quieter, but they cause similar headaches:

`===` disagrees.
Almost nobody puts them there on purpose. They arrive by paste: from a word processor, a shared doc, a web page, a chat message, or an old template that was itself pasted from somewhere else. Nothing on screen shows them, so they hitchhike from campaign to campaign like a burr on a sock.

If you build or send email from code, that means your templates, your CMS fields and your copy pipeline are all possible entry points.

Most editors and every language with a regex engine can search for them. This pattern matches the direction controls and the common zero-width characters:

```
[\u200B\u2060\u202A-\u202E\u2066-\u2069]
```

A quick Node check over a template string:

``` js
const INVISIBLE = /[\u200B\u2060\u202A-\u202E\u2066-\u2069]/g;

function findInvisible(text) {
  return [...text.matchAll(INVISIBLE)].map(m => ({
    index: m.index,
    codepoint: 'U+' + m[0].codePointAt(0).toString(16).toUpperCase().padStart(4, '0'),
  }));
}

console.log(findInvisible('Welcome\u200B aboard'));
// [ { index: 7, codepoint: 'U+200B' } ]
```

A hit in text that's meant to be plain English is almost always safe to delete. Run it in CI against your email templates and you'll catch the next paste before a customer does.

Here's where the "just delete all of it" instinct goes wrong. Two invisible characters are load-bearing in real writing:

That's why the regex above deliberately leaves out U+200C and U+200D. Strip them and you haven't cleaned your Persian newsletter, you've misspelled it.

And real right-to-left text (Arabic, Hebrew) doesn't need override characters at all. The standard HTML `dir` attribute on a block, with embedded left-to-right runs such as prices and URLs isolated, gets direction right without any invisible controls in the text.

This is the approach Email Digit takes, described in its guide to [invisible Unicode characters in email](https://emaildigit.com/guides/invisible-unicode-characters-in-email): when an email is rendered for sending, the text of paragraphs, quotes and table cells is cleaned. Direction controls, the zero-width space, the word joiner and a few other invisible formatting codes such as the soft hyphen are removed, while the joiner and non-joiner are kept.

The guide is also upfront about the limits, which are worth copying into your own mental model whatever tool you use:

`dir="rtl"` on blocks instead of override characters.
If a character is invisible and changes what a line means, it doesn't belong in an email. Your diff tool can't see it. Now your CI can.

*I work on [Email Digit](https://emaildigit.com), a deliverability-first email platform. Sources: Email Digit's guide on invisible Unicode characters (read November 12, 2026) and the Trojan Source CVE record.*

*This post was drafted with AI assistance and checked against the sources above.*
