HTML Encoding Explained
HTML has a fundamental ambiguity problem, and entities are how it is solved. If you have ever wondered why you must write & instead of a plain ampersand, or why < exists at all, the answer is the same in both cases — and understanding it turns out to be one of the most security-relevant things a web developer can know.
The ambiguity
HTML uses certain characters as markup. The angle bracket <means “a tag starts here.” That is its job.
So what happens when you want to write about the less-than sign — to say “if x < 5”? The browser sees <, assumes a tag is beginning, and tries to parse < 5” as one. Your text vanishes or the page breaks.
The browser cannot tell whether you meant markup or content. HTML encoding resolves it by replacing the character with a stand-in — an entity— that unambiguously means “display this character; do not interpret it.”
The five that matter
There are hundreds of entities, but only five are genuinely required:
<→<(less than)>→>(greater than)&→&(ampersand)"→"(double quote)'→'(single quote / apostrophe)
Every entity starts with & and ends with ;. That structure is what makes the ampersand itself special.
Why the ampersand must be escaped too
This is the part that feels circular but is actually elegant.
Since every entity begins with &, the browser must treat an ampersand as “an entity may be starting.” So a raw & in your text is ambiguous — is it a literal ampersand, or the start of something?
Write Tom & Jerry and most browsers will forgive you. But write Salt & Pepper carelessly, or a URL containing ©=1, and the browser may render © — because © is the copyright entity. Your parameter has become a symbol.
Hence the rule: encode the ampersand first, always. If you escape it last, you will re-escape the ampersands you just introduced.
The real reason this matters: XSS
Everything above sounds like a formatting nicety. It is not. HTML escaping is the primary defence against one of the most common vulnerabilities on the web.
Cross-Site Scripting (XSS) works like this. Your site takes user input — a comment, a username, a search term — and displays it on a page. An attacker submits, instead of a comment:
<script>steal(document.cookie)</script>
If you insert that into the page without escaping, the browser does not see text. It sees a script tag— and it runs it. In every visitor's browser, with full access to their session, their cookies and their logged-in account. The attacker has executed their code on your site, under your domain.
Now escape it. The < becomes <, and the browser renders the harmless text“<script>steal(document.cookie)</script>” on the page. Ugly, but inert. The attack is dead.
The rule: escape all user-supplied content before rendering it as HTML. Every time, with no exceptions. The overwhelming majority of XSS vulnerabilities are simply somebody forgetting this once.
Context matters
Escaping is not one-size-fits-all — the correct escaping depends on where the value lands:
- In HTML body text — standard entity escaping is what you need.
- In an attribute — quotes must be escaped too, or an attacker can close your attribute and add their own (
onmouseover=...). Always quote attributes. - Inside a
<script>block — HTML escaping is not sufficient. You need JavaScript escaping. This is a common and dangerous mistake. - In a URL — that is a different scheme entirely: percent-encoding, not entities.
Modern frameworks — React, Vue, Angular, and most template engines — escape by default, which has quietly eliminated a huge class of bugs. The danger now lives in the escape hatches: anything named like dangerouslySetInnerHTML or v-html deliberately bypasses the protection. Those are the places to look when auditing.
The double-encoding trap
Encode something twice and the entities themselves get encoded:
- Original:
<b> - Encoded once:
<b> - Encoded again:
&lt;b&gt;
The page then literally displays <b> to the reader. If you are seeing raw entity codes on a live page, something escaped an already-escaped value. Escape exactly once, at the moment of output.
Other useful entities
— non-breaking space, which prevents a line break at that point.©→ © ·®→ ® ·™→ ™—→ — ·–→ –…→ …
With UTF-8 now universal, you can usually just type © or — directly. Entities remain essential for the five reserved characters, and handy for .
Frequently asked questions
Is HTML encoding a form of security? Yes — for output. It is the standard defence against XSS. But it is not encryption and provides no secrecy.
Is HTML encoding the same as URL encoding? No. They solve similar problems in different contexts: entities (<) for HTML, percent-encoding (%20) for URLs.
Do I still need entities if my page is UTF-8? Yes, for < > & " '. UTF-8 solves character representation, not markup ambiguity.
Why do I see &amp; on a page? Double encoding.
Encode HTML now
Use our HTML Encoder/Decoder to escape and unescape HTML in your browser — nothing is uploaded. For the URL equivalent, see the URL Encoder and our guide on URL encoding.