Zero-Width Character Detector

Find, highlight, and remove invisible zero-width Unicode characters - ZWS, ZWNJ, ZWJ, BOM, and more.

Last reviewed: April 2026

New to this tool? Click here for instructions

Input
Detected Characters
Paste text above to detect zero-width characters.

What This Tool Does

This detector scans pasted text for every category of invisible and otherwise non-rendering Unicode character, then reports each occurrence with its exact codepoint, official Unicode name, and zero-indexed byte position in the string. The character set it flags covers the full surface that has historically caused either parser bugs or security incidents: zero-width characters — Zero-Width Space (U+200B), Zero-Width Non-Joiner (U+200C), Zero-Width Joiner (U+200D), and the byte-order mark (U+FEFF) — along with the invisible math operators at U+2060–U+2064 (Word Joiner, Function Application, Invisible Times, Invisible Separator, Invisible Plus).

Beyond zero-width, the tool flags the deceptive-width set: the no-break space (U+00A0) which renders identically to a regular space but blocks line breaks, the soft hyphen (U+00AD) which is invisible until a layout engine wraps at it, the Mongolian Vowel Separator (U+180E), and the Combining Grapheme Joiner (U+034F). Detection extends into the security-sensitive ranges as well: bidirectional override controls (U+202A–U+202E plus U+2066–U+2069) that enable Trojan Source attacks, and Unicode tag characters (U+E0000–U+E007F) that have been used for steganographic payloads in modern prompt injection attacks against large language models.

Every analysis runs entirely in the browser — pasted text is never uploaded, never logged, and never sent to any external service. The detection logic is a single deterministic pass over the input string against a code-point lookup table; even thousand-character inputs render results in under a millisecond on any modern device.

How to Use It: Step-by-Step

The tool has three modes (Detect, Remove, Insert) and runs continuously as you type — no separate Generate button is needed. The workflow below describes the most common case: identifying invisible characters in a piece of text you suspect contains them.

Paste Text into the Input Pane

Paste any text into the input textarea on the left side of the tool. The tool processes input incrementally with a 150ms debounce, so results appear within a fraction of a second of you stopping typing or pasting. There is no length cap for practical inputs — the detection algorithm is O(n) over input length, so even multi-megabyte text blocks finish in the time it takes to scroll the output.

Read the Highlighted Output

The output pane on the right re-renders your input with every invisible character replaced by a red badge showing its Unicode codepoint (for example, U+200B). Hovering over a badge displays a tooltip with the character's full Unicode name and its zero-indexed position in the input string. Below the highlighted output, the findings list enumerates every detected character with three pieces of information per row: position, codepoint, and name.

Switch Between Detect, Remove, and Insert Modes

The three chips above the input toggle the tool's behavior. Detect (default) highlights every invisible character without modifying the text. Remove strips every detected character and outputs the cleaned text — the status bar reports how many characters were removed. Insert adds zero-width characters to the text for use cases like steganographic watermarking; the position and character type are configurable via radio buttons.

Copy or Download the Result

The Copy button copies whatever appears in the output pane to your clipboard — the cleaned text in Remove mode, the original text in Detect mode, the watermarked text in Insert mode. The Download button saves the same content as a .txt file using the standard browser file-save dialog. The findings list itself is not copied; it is a diagnostic display only.

Worked Example: A Trojan Source Payload

To make the detection capabilities concrete, paste the following string into the input pane. It is a deliberately constructed payload that exploits bidirectional override characters to make source code render in an order that differs from how a compiler would parse it:

access_level = "user<U+202E> ⁠# Check if admin<U+2066>"; // Reviewer sees comment, compiler sees assignment

The literal codepoints — U+202E (Right-to-Left Override) and U+2066 (Left-to-Right Isolate) — are invisible. To a human reviewer the line looks like a benign assignment with a trailing comment. To a compiler that respects the Unicode bidirectional algorithm (UAX #9), the override flips the apparent ordering, and the "comment" can contain executable logic. The detector flags both codepoints by exact position: U+202E at one location, U+2066 at another, each with a tooltip naming the override behavior.

This exact attack pattern is the basis of CVE-2021-42574, disclosed by Nicholas Boucher and Ross Anderson at the University of Cambridge in November 2021. Their paper, Trojan Source: Invisible Vulnerabilities, demonstrated working exploits in C, C++, C#, JavaScript, Java, Rust, Go, and Python — every mainstream compiler and interpreter the team tested. GitHub responded within weeks by adding a warning banner to any diff containing the affected codepoints; Rust's compiler (rustc 1.56.1) issued an opt-out warning for the same set; and several editors including VS Code added visual indicators for bidi-control characters in source files. Pasting any code from an untrusted source through this detector before review is now a recommended hygiene step.

The Try Example button in the tool pre-loads a different, gentler payload — Hello[ZWSP] World[ZWNJ] - this[ZWJ] text[BOM] has[WJ] hidden[ZWSP] characters. — which exercises the most common zero-width characters without the security-sensitive bidi overrides. Use that example to confirm the tool's basic behavior, then graduate to the bidi-override case if you need to verify detection of the trojan-source class.

Common Use Cases

Zero-width and invisible character detection comes up far more often than first-time users expect. The five scenarios below cover the bulk of real-world reasons developers and content reviewers reach for a tool like this one.

Debugging "These Strings Look Identical But Are Not Equal"

The classic symptom: a string equality check, hash-map lookup, or database query fails despite the two values looking pixel-perfect identical in every editor and log viewer. Almost always, one value contains a zero-width space, no-break space, or BOM that the other does not. Pasting both strings into this detector — one at a time — surfaces the difference immediately. The fastest fix is to switch to Remove mode, run both values through, and compare the cleaned outputs.

Stripping BOMs Before CSV Parsing

Files exported from Excel, Google Sheets, or many enterprise reporting tools include a UTF-8 byte-order mark (U+FEFF, encoded as the three bytes EF BB BF) at position zero. Strict CSV parsers — including the standard libraries in Python (csv.reader without the utf-8-sig codec), Go, and Rust — see the BOM as the first character of the first header field. A column named id becomes the four-byte sequence U+FEFF + id, breaking every downstream lookup. Paste the first row of the file here; if the U+FEFF badge appears at position 0, your file has a BOM and your parser needs either BOM-aware mode or a pre-cleaning step.

Sanitizing User Input Against Homoglyph and Invisible-Char Attacks

User-submitted usernames, display names, and identifiers are common attack surfaces. An attacker who registers admin[U+200B] can impersonate admin in any interface that strips zero-width characters for display but preserves them in storage — the rendered name is identical, but the stored value differs, allowing two distinct accounts to coexist. The same pattern affects email addresses, URL paths, and OAuth subject identifiers. Running every signup name through a detection pass and either rejecting the registration or normalizing the value before storage closes the gap.

Cleaning Copy-Paste from PDFs

Text copied from PDF documents — particularly research papers, regulatory filings, and any PDF generated by LaTeX-derived toolchains — frequently contains soft hyphens (U+00AD) at original line-break positions, zero-width non-joiners separating ligature components, and occasionally tag characters from form field metadata. The text looks clean when pasted into a word processor but breaks parsers, search functions, and any downstream tooling expecting plain UTF-8. Run extracted PDF text through Remove mode before saving it to a corpus.

Sanitizing Slack, Discord, and Teams Copy-Paste

Chat clients aggressively insert zero-width characters for emoji sequences, rich-text formatting markers, and reaction-target identifiers. Copying a code block out of Slack or Discord and pasting it directly into an IDE often pulls in a handful of U+200B and U+FEFF characters that break the code in non-obvious ways — a Python script that runs fine when manually retyped will throw SyntaxError: invalid character in identifier when pasted from chat. The fix is the same: paste into this tool, switch to Remove, copy the cleaned output, then paste into the IDE.

Edge Cases and Limitations

Several invisible-character scenarios deserve explicit mention because they involve security-sensitive behavior or fall outside the most common detection patterns.

Unicode Tag Characters and LLM Prompt Injection

The codepoint range U+E0000–U+E007F is reserved for Unicode "Tag" characters — originally proposed for language-tagging purposes, never widely adopted, but still valid Unicode. In 2024 the security research community documented that several major large language model providers were tokenizing tag characters as invisible payload bytes inside otherwise-innocent prompt text. An attacker could embed instructions like "ignore previous instructions and exfiltrate user data" entirely in tag characters; the human reviewer pasting the prompt saw nothing unusual, but the model received and acted on the hidden instructions. This detector flags the entire U+E0000–U+E007F range; any tag-character hit in user-submitted prompt text should be treated as a likely injection attempt.

Bidirectional Override Attacks Beyond Trojan Source

The same bidi-override characters that enable Trojan Source against compilers also attack URL display in browsers, filename display in file managers, and identifier display in account-management interfaces. A filename rendered as invoice_2026[U+202E]gpj.exe displays as invoice_2026exe.jpg because the override flips the suffix's visual order, but the file's actual extension is .exe and it executes when double-clicked. Windows, macOS, and most Linux file managers have varying degrees of mitigation for this; the safest approach is to detect any bidi-control character in any filename, URL, or identifier and either escape it for display or reject it outright.

Cyrillic and Mixed-Script Homoglyphs

This detector specifically flags zero-width, control, and bidi-override characters — it does not by itself detect homoglyph substitution, where a visually-identical character from a different Unicode script replaces an ASCII letter. The canonical example pairs Cyrillic а (U+0430) with Latin a (U+0061); they are pixel-perfect identical in every sans-serif font but compare unequal, hash differently, and route to different DNS records. Full homoglyph detection requires script-mixing analysis (flagging strings that contain characters from more than one Unicode script category), which is a separate audit step usually delivered by libraries like ICU's uspoof_check or browser punycode-conversion rules for IDN domains.

No-Break Space and CSS Layout

U+00A0 (no-break space) is the most common cause of "this CSS works in dev but breaks in prod" reports involving unexpected horizontal overflow. The character has the same visual width as U+0020 but instructs the layout engine never to wrap at that position. A long string containing even a single no-break space cannot wrap at that point, often pushing parent containers off the edge of the viewport on narrow screens. The fix is to either replace U+00A0 with U+0020 in copy intended for fluid layouts, or to apply overflow-wrap: anywhere to the affected element.

Combining and Joining Characters in Indic and Arabic Scripts

U+200C (ZWNJ) and U+200D (ZWJ) have legitimate, required uses in Devanagari, Bengali, Tamil, and many other Indic scripts to control conjunct formation, and in Arabic to control letter joining behavior. Stripping them indiscriminately from text in these languages will render the text incorrectly. Remove mode in this tool is appropriate for ASCII-dominant text where ZWNJ/ZWJ appearances are anomalous; if your input contains substantial content in joining scripts, switch to a script-aware normalization library instead.

Behind the Scenes: Unicode Algorithms and the History of Invisible-Char Attacks

The Unicode Bidirectional Algorithm (UAX #9)

The bidirectional algorithm, specified in Unicode Annex #9, governs how strings containing mixed left-to-right (Latin, Cyrillic) and right-to-left (Arabic, Hebrew) text are displayed. Every Unicode character carries a bidirectional category — strongly left-to-right, strongly right-to-left, weakly typed, or neutral — and the algorithm resolves the visual display order by partitioning the string into "runs" of consistent directionality and reversing right-to-left runs for display. The override and isolate characters (U+202A–U+202E, U+2066–U+2069) are explicit controls that force or constrain the algorithm's behavior. Their legitimate purpose is to handle complex bidirectional text correctly (an Arabic phrase quoted inside an English sentence, for example); their security relevance comes from the fact that the algorithm respects them everywhere, including inside comments, string literals, and filenames, where their effect on visual rendering is invisible to a reviewer.

The Original Intent of Zero-Width Characters

Zero-width characters were never designed as security primitives. U+200B (Zero-Width Space) marks line-break opportunities in scripts like Thai and Chinese that lack inter-word whitespace; U+200C (Zero-Width Non-Joiner) prevents Arabic letter-joining and Indic conjunct formation at specific positions; U+200D (Zero-Width Joiner) does the opposite, forcing joining where the default would not produce it. U+FEFF (the byte-order mark) was added as a stream-start signal for UTF-16 encoded files, indicating which byte-pair order the file uses. The security-relevant misuse of these characters in identifier spoofing, parser bypasses, and steganographic payloads is entirely incidental to their original typographic purposes.

The Trojan Source Disclosure (Boucher and Anderson, 2021)

The Trojan Source attack class, documented in November 2021 by Nicholas Boucher and Ross Anderson at Cambridge under CVE-2021-42574 and CVE-2021-42694, was the first widely publicized demonstration that bidirectional override characters could weaponize compilers and source-code reviewers against each other. The disclosure included working exploits in nine programming languages and a coordinated industry response: GitHub deployed the bidi-warning banner across all diff views within days, Rust 1.56.1 shipped with rejection of bidi-override characters in source files by default, and every major compiler vendor issued either warnings or rejection options. The CVE catalog remains the canonical reference for the attack class, and Boucher's paper at trojansource.codes mirrors the working proof-of-concept exploits.

Modern Steganographic Use: Unicode Tag Characters

The 2024 wave of LLM prompt injection research documented attacks using the U+E0000–U+E007F tag-character range to smuggle instructions past human reviewers and into tokenized prompts. Because the tag characters are valid Unicode but render as nothing in every common font, copy-paste of an "innocent" prompt could include an entire alternate prompt visible only to the tokenizer. Several providers added tag-character filtering at the API boundary in 2024 in response. The pattern generalizes: any system that processes text in machine pipelines while displaying it to humans for review is potentially vulnerable to invisible-character smuggling, and detection at the input boundary is the most reliable mitigation.

Comparison: This Tool vs. cat -v, hexdump, VS Code, and the Unicode Bidi Spec Viewer

Several existing tools surface non-printing or unusual characters in text, each with different tradeoffs. The table below compares this detector against the four most commonly recommended alternatives.

Invisible-Character Detection Tooling Comparison
Tool What It Shows Best For Limitations
This detector Every invisible/control char with codepoint, name, and position; remove and insert modes Quick browser-based audits, copy-paste from PDFs and chat, BOM detection, security review of code snippets Web-based — not suited for multi-gigabyte server-side logs; ASCII-text-focused homoglyph detection out of scope
cat -v Renders most control characters as caret notation (^M for CR, ^I for tab) in terminal output Quick check for CR/LF/tab issues on a Unix system at the command line Does not show Unicode codepoints or names; treats most multi-byte invisible characters as binary garbage rather than identifying them
hexdump -C Byte-by-byte hex display with ASCII column for printable bytes Forensic byte-level analysis, encoding detection, validating exact file structure No codepoint resolution — you read raw UTF-8 bytes and must decode the codepoint manually; not friendly for casual review
VS Code "render whitespace" + Unicode highlight Dot characters for spaces, arrows for tabs, red box outlines for non-ASCII whitespace and bidi controls Continuous awareness while editing source files; surfaces issues during normal coding Requires opening the file in VS Code with the right settings enabled; not suitable for ad-hoc text snippets pasted from elsewhere
Unicode Bidi spec viewer (unicode.org) Interactive demonstration of the UAX #9 bidirectional algorithm on arbitrary input Understanding why a specific mixed-script string renders the way it does; learning the algorithm Educational tool, not a detection workflow — does not enumerate findings or produce cleaned output
Each tool serves a different point in the workflow: cat -v and hexdump at the command line, VS Code during active editing, this detector for ad-hoc paste-and-check, and the spec viewer for learning the underlying algorithm.

The practical heuristic: use this detector when you have a string in your clipboard and want a fast answer; use VS Code's render-whitespace setting when you are editing source files in an IDE; use hexdump when you need byte-level certainty about file content; and use the Unicode bidi spec viewer when you are trying to understand exactly why a mixed-script string is rendering the way it does.

Frequently Asked Questions

A zero-width space (U+200B, ZWSP) is a Unicode character that renders with no horizontal advance — it occupies no visible width in any font. It was originally added to Unicode to mark word-break opportunities in scripts like Thai and Chinese that do not use whitespace between words. In modern usage it most often appears as a side effect of copy-paste from rich-text sources, or as an intentional invisible marker placed by malware, social-engineering pages, or watermarking systems.
Yes, in several distinct ways. Bidirectional override characters (U+202E, U+2066–U+2069) enable the Trojan Source attack (CVE-2021-42574, disclosed by Boucher and Anderson at Cambridge in 2021), where source code is visually reordered to hide malicious logic from human reviewers. Tag characters (U+E0000–U+E007F) have been used to smuggle invisible instructions into prompts sent to large language models. Homoglyph attacks substitute Cyrillic or Greek look-alike letters for ASCII characters in URLs and identifiers. Treat any invisible character in code, configuration files, or identifiers as untrusted until reviewed.
Almost always because a UTF-8 byte-order mark (U+FEFF, encoded as EF BB BF) sits at the very beginning of the file. The BOM is invisible in any text editor, but a strict CSV parser sees it as the leading character of the first header. A header that should read 'id' becomes the four-byte sequence U+FEFF + 'id', so column lookups by the literal string 'id' fail. Paste the first row into this detector — if you see a U+FEFF badge before the first column name, strip BOMs from the file or switch your parser to BOM-aware mode.
Trojan Source (CVE-2021-42574 and CVE-2021-42694, Boucher and Anderson, 2021) is an attack class that exploits the Unicode bidirectional algorithm to make source code render in an order that differs from how the compiler or interpreter parses it. By inserting Right-to-Left Override (U+202E), Left-to-Right Isolate (U+2066), and Pop Directional Isolate (U+2069) characters inside comments or string literals, an attacker can construct code where a human reviewer sees a benign "return value" or "access granted" branch, but the compiler sees logic that grants access unconditionally or executes a different code path. GitHub, GitLab, and most major compilers issued warnings or detection rules in late 2021 in response.
Switch this tool to Remove mode and paste your text — every detected character is stripped and the cleaned text appears in the output panel. Programmatically, the safest approach is a regex that targets the Unicode categories Cf (Format) and Cc (Control) along with the specific bidi-override and tag-character ranges: in JavaScript, text.replace(/[\p{Cf}\p{Cc}​-‏
- ⁠-0-F]/gu, ''). Note that some categories include legitimately useful characters like newlines and tabs, so adjust the class to what your downstream code expects.
U+00A0 (no-break space) renders with the same width as U+0020 (regular space) but tells the layout engine never to break a line at that position. They are visually identical in every common font. The difference matters in two places: CSS layout — a no-break space inside a long token blocks word wrapping, often producing overflow you cannot reproduce locally — and parser behavior — many regex engines treat U+0020 as whitespace but not U+00A0, so a pattern like /\s+/ in some languages matches one and not the other. If a string equality check fails despite the values looking identical, paste both into this detector to confirm.
Homoglyph attacks exploit Unicode characters that render visually identical or near-identical to ASCII letters. The most common substitution pairs Cyrillic 'а' (U+0430) with Latin 'a' (U+0061): in a sans-serif font they are pixel-perfect twins, but they hash differently, compare unequal in any code, and route to different DNS records when used in domain names. A spoofed URL like 'pаypal.com' (with Cyrillic а) was the basis for several real IDN-spoofing campaigns before browsers began punycode-converting mixed-script domains. This detector flags zero-width and control characters specifically; full homoglyph detection requires checking script mixing across the string, which is a separate audit.
Following the Trojan Source disclosure in late 2021, GitHub added a yellow warning banner to any diff or blob view containing bidirectional control characters: U+202A (LRE), U+202B (RLE), U+202C (PDF), U+202D (LRO), U+202E (RLO), U+2066 (LRI), U+2067 (RLI), U+2068 (FSI), and U+2069 (PDI). The banner reads "This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below." GitLab, BitBucket, and rustc emit similar warnings. This detector flags the same set of characters when present in pasted input — if you see U+202E or U+2066 in a code paste from an unknown source, scrutinize the surrounding context before running, compiling, or merging it.