HTML Entities: Why They Exist and How Far to Escape
HTML entities tell the browser to display a character literally rather than interpret it as markup. < renders as < on screen without starting a tag. That distinction isn't just a notation detail — it's the underlying principle of XSS defense.
The five characters that must be escaped
Only a handful of characters carry special meaning in HTML. Handle these five correctly and you're safe in most contexts.
| Character | Named entity | Numeric | Why it must be escaped |
|---|---|---|---|
| < | < | < | Read as the start of a tag |
| > | > | > | Read as the end of a tag |
| & | & | & | Read as the start of an entity |
| " | " | " | Terminates a double-quoted attribute value early |
| ' | ' | ' | Terminates a single-quoted attribute value early |
Why libraries avoid '
The named entity ' is defined in HTML5 and XML but not in HTML4, so older parsers and some email clients render it as the literal text '. The numeric reference ' works everywhere, which is why escaping libraries conventionally emit ' instead.
How to use it
- Paste the text to escape, or a string containing entities you want to reverse.
- Encode converts special characters into entities; Decode turns entities back into characters.
- If the string is going into HTML source, use the Encode result.
- If scraped data contains artifacts like &lt;, run Decode to clean it up.
Escaping is context-dependent
Entity encoding is only sufficient for the 'HTML text' context. The same value placed in a different context needs different rules, and conflating them leaves vulnerabilities open even though you 'escaped' the input.
- HTML body text: escape the five characters above. That's what this tool does.
- Attribute values: always wrap in quotes and escape that quote character. Unquoted attributes can be attacked with nothing but a space.
- Inside <script>: entities are not interpreted there. Use JavaScript string escaping or JSON.stringify, and neutralize the </script> sequence separately.
- URL attributes (href, src): entities alone don't stop a javascript: scheme. You need scheme allowlisting.
- Inside CSS: CSS has its own escaping rules.
The double-encoding problem
You'll often see &lt; — an ampersand that got encoded a second time. It happens when an already-escaped string is escaped again, and the result is highly visible: users literally see the characters < on the page.
The cause is almost always overlapping layers: a template engine that auto-escapes plus application code that escapes again, or escaped values stored in the database and escaped once more on output. The rule is store raw, escape once immediately before output. Modern frameworks — React, Vue, Django, Rails — auto-escape by default, so manual escaping on top of them is what creates the bug.
Common uses
- Putting code examples in a blog post without the tags actually rendering
- Cleaning leftover artifacts like &#39; out of scraped content
- Injecting dynamic values such as usernames into HTML email templates
- Handling & and < in data being exported to CSV or XML
- Recovering the original text from double-encoded legacy data
Frequently Asked Questions
- Does entity encoding alone prevent XSS?
- For inserting text into HTML body content it's largely sufficient, but it does not cover XSS as a whole. Attribute values, script bodies, href URLs, and inline CSS each demand different escaping. And if your feature genuinely needs to render user-supplied HTML — a rich text editor, say — the answer isn't escaping at all but sanitizing with something like DOMPurify to keep only allowed tags.
- Is different from a normal space?
- Yes. A non-breaking space (U+00A0) prevents a line break at that point and isn't collapsed the way runs of ordinary spaces are. It gets abused for layout spacing, which belongs in CSS margin or padding instead. It also compares as a different character in text search and equality checks, which is a recurring source of bugs.
- Named entities or numeric references?
- In HTML documents, named entities like < read more clearly. When exporting to XML or XML-derived formats, numeric references like < are safer: XML predefines only five named entities (lt, gt, amp, quot, apos), so things like cause parse errors.
- Should I escape Korean text or emoji?
- No need. If your document is UTF-8 and declares <meta charset="utf-8">, all non-ASCII characters can be written literally. Converting them to entities bloats the file and hurts readability. It's only worth considering in very old environments where you cannot set the character encoding.
- Does decoding restore the exact original?
- Usually, but round-tripping isn't guaranteed. A and A decode to the same thing, so you cannot tell which was in the source. Likewise, with a double-encoded string there's no way to know from the data alone how many decode passes are correct.
- Do I need to escape manually in React?
- No. Strings interpolated into JSX are escaped automatically, and adding entities by hand makes the literal characters < appear on screen. The exception is dangerouslySetInnerHTML, where what you need is sanitizing rather than escaping.
💡 Note: If characters like < or & are visible on your page, escaping ran twice. Start by counting how many layers are escaping the value.