What a homoglyph is
Look at these two words: “paypal” and “pаypal”. On screen, in almost any font, they are indistinguishable. One of them is not what it appears to be. The second word’s third letter isn’t the Latin letter “a” at all — it’s the Cyrillic letter “а,” a completely different character that Unicode happens to render as an identical glyph in most typefaces. A human reading either word sees the same shape and moves on. A computer comparing the two strings sees two unrelated sequences of bytes that share nothing but appearance.
That gap between what a character looks like and what it actually is has a name: a homoglyph. And the gap is exactly wide enough to build an attack in.
Why homoglyph attacks work
Unicode encodes over a hundred thousand characters, drawn from scripts all over the world, and a fair number of them were never going to look distinct from each other. Cyrillic and Greek both evolved from the same ancestor as the Latin alphabet, so plenty of their letters ended up shaped like Latin ones almost by historical accident. Add fullwidth Latin characters, originally designed to line up neatly in fixed-width East Asian text layouts, and you have three separate blocks of Unicode that can stand in for ordinary “a,” “e,” “o,” “p,” and “c” without a font rendering any visible difference.
None of this is a flaw in Unicode. It’s a side effect of trying to represent every writing system humanity uses, in one encoding, without requiring every letter to look unique across all of them. The problem only appears when someone exploits the overlap on purpose.
What a homoglyph attack looks like
The classic version is a spoofed domain name. An attacker registers a domain where one letter has been swapped for its homoglyph — “microsоft.com” with a Cyrillic “о,” say — and sends it in a phishing email. The link text, the domain in the browser’s address bar, and the certificate warning (there usually isn’t one, since the attacker can get a valid certificate for their own real domain) all look completely ordinary. Browsers have gotten better at flagging mixed-script domains over the last decade, but the underlying trick still works in plenty of contexts that browsers don’t police: Slack messages, internal chat tools, README files, package names, and plain-text email.
The same technique works on identifiers, not just domains. A homoglyph swapped into a username, a product SKU, or a config key can create two entries that look identical in a list but are actually different keys — one legitimate, one an attacker’s. Anyone skimming the list sees no problem, because skimming is a visual process and the defect isn’t visual.
Why spellcheckers miss homoglyphs
A homoglyph substitution doesn’t produce a misspelling. “Pаypal” isn’t a typo — every letter is still recognizably in the right place, doing the right job visually. Spellcheckers, grammar tools, and a human proofreader all work by pattern-matching what a word looks like against what a correct word should look like, and a homoglyph passes that check perfectly. The only way to catch it is to stop asking “does this look right” and start asking “is every character actually the character it appears to be” — which means checking code points, not glyphs. That’s a fundamentally different question, and it’s why this kind of check belongs in a dedicated tool rather than a proofreading pass.
The false-positive problem, and how to avoid it
A checker that flagged every Cyrillic or Greek letter as suspicious would be useless the moment someone pasted in a genuinely Russian or Greek document — which is not an attack, just a different language. The fix is to look at proportion, not presence. A handful of foreign-script letters scattered through an otherwise Latin document is worth flagging; a document that’s mostly Cyrillic is a Cyrillic document, and flagging every letter in it would bury the one finding that actually matters under hundreds that don’t. A homoglyph checker earns trust the same way any finding-based tool does: by staying quiet on the cases that are actually fine, so the cases it does flag are worth taking seriously.
There’s a mirror-image edge case worth naming too: a very short piece of text with just one substituted letter can accidentally cross a percentage threshold built for whole documents, purely because the sample is small. A ten-letter domain with one homoglyph is 10% “foreign” by simple arithmetic, which is exactly the kind of short input a homoglyph checker most needs to catch. Getting this right means requiring a reasonably sized sample before trusting the ratio at all, not just picking a cutoff and applying it uniformly regardless of input length.
How to protect against homoglyphs
If you’re pasting a domain, an identifier, or a short string you don’t fully trust, run it through a character-level checker before you click it, register it, or paste it into a config file. If you’re reviewing a pull request that touches identifiers, usernames, or hardcoded strings, treat an unexplained non-ASCII character the same way you’d treat an unexplained dependency: worth a question, not an automatic assumption of malice, but not something to wave through unread either.
The broader lesson generalizes past security. Plenty of real defects in text are invisible to a visual read precisely because they don’t change how anything looks — see expanding acronyms on first use for a completely different example of the same underlying pattern: a problem that requires comparing the document against itself, or a string against its own byte values, rather than just reading it. Whenever a defect lives one level below what your eyes are actually checking, the fix is the same: stop reading and start scanning.
Check your own text
The invisible & problem character detector scans pasted text for homoglyphs alongside zero-width characters, non-standard spaces, and other characters that look identical to something else or don’t look like anything at all. Paste a suspicious domain, a document, or a username list to see exactly which characters aren’t what they appear to be.