Complete HTML Entity Cheat Sheet for Special Characters and Web Security

Quick Win: The Three Entities That Stop 80% of XSS Attempts

If you're rendering user-generated content and only escaping < and >, you're leaving the door wide open. The minimum viable entity set for HTML body context is actually three: &lt; for <, &gt; for >, and &amp; for &. That last one—&amp;—is the entity most developers skip, and it's exactly what lets attackers break out of your existing entities to inject malicious markup.

Here's the scenario that probably sounds familiar: you're building a feature that displays user comments, product reviews, or CMS-authored content. You know you need to handle special characters. You know XSS is a real threat. But every time you sit down to implement encoding, you end up Googling "HTML entity cheat sheet" and falling down a rabbit hole of 200-row reference tables that tell you everything except what actually matters for security.

This is that cheat sheet—stripped down to what you actually need. No fluff, no exhaustive lists of emoji entities you'll never use. Just the entities, the contexts, and the workflow that'll let you ship user content safely without spending your afternoon reading W3C specs.

The Core Cheat Sheet: Entities You'll Actually Use

Let's start with the reference table you'll come back to. These are the entities that matter for 95% of real-world web development work—organized by what they do, not alphabetically.

Security-Critical Entities (Memorize These)

These five entities are your frontline defense against XSS. If you encode nothing else, encode these:

  • &amp; → renders as & — The ampersand. Must be encoded first, always, in every context. If you skip this, an attacker can craft input like &lt;script&gt; that double-decodes into executable markup.
  • &lt; → renders as < — Less-than sign. Prevents injection of HTML tags.
  • &gt; → renders as > — Greater-than sign. Closes the door on tag injection from the other direction.
  • &quot; → renders as " — Double quote. Critical for attribute context encoding.
  • &#39; → renders as ' — Single quote. Use the numeric form because &apos; isn't supported in all browsers.

Display Entities for Common Special Characters

These are the characters you need to render correctly without breaking your layout or triggering parser quirks:

  • &nbsp; → Non-breaking space. Useful, but don't overuse it—stacking five &nbsp; entities for indentation is a code smell. Use CSS margin instead.
  • &copy; → © (copyright symbol)
  • &reg; → ® (registered trademark)
  • &trade; → ™ (trademark)
  • &mdash; — (em dash)
  • &ndash; – (en dash)
  • &hellip; … (ellipsis)
  • &laquo; / &raquo; → « » (angle quotes, common in European-language content)

Numeric vs. Named: When It Matters

Every named entity has a numeric equivalent. &lt; and &#60; produce the same character. For security encoding, prefer numeric entities—they're universally supported and don't depend on the browser's entity dictionary. For display characters like &copy; or &mdash;, named entities are fine and more readable in your source code.

Why Context Determines Which Entity You Need

Here's where most cheat sheets fail you: they list entities without explaining that where you put a character matters as much as what the character is. Encoding a double quote with &quot; is essential inside an HTML attribute, but overkill in a text node. Encoding < is critical in both—but for different reasons.

Think of it this way: the HTML parser has different "modes" depending on what it's currently parsing. When it's reading text content between tags, it's in one mode. When it's reading an attribute value, it's in another. When it's inside a <script> block, the rules change entirely. Your entity encoding strategy needs to match the parser's current context—or you'll either under-encode (creating a vulnerability) or over-encode (creating mojibake on the page).

The Double-Encoding Trap

One of the most common bugs I see: a CMS double-encodes content because two different layers each run escapeHtml(). The user types <hello>, the first layer turns it into &lt;hello&gt;, the second layer turns it into &amp;lt;hello&amp;gt;, and the browser renders &lt;hello&gt; as literal text. The user sees &lt;hello&gt; instead of <hello>.

This isn't just a cosmetic issue. Double-encoding can mask XSS payloads by making them appear safe at one layer while remaining dangerous at another. The fix is simple but requires discipline: encode once, at the boundary closest to the output. We'll cover exactly how to do that in the workflow section below.

The Five Encoding Contexts Every Developer Must Know

If you remember nothing else from this article, remember this: there are five distinct contexts where user content can end up in your HTML, and each requires a different encoding strategy. Miss one, and you've got a vulnerability.

1. HTML Text Node Context

This is the most common scenario: user content placed between tags, like <p>USER_CONTENT</p>. Here you need to encode &, <, and >. That's your minimum. Quotes don't need encoding here because the parser isn't looking for quote-delimited values.

2. HTML Attribute Value Context

Content placed inside an attribute, like <input value="USER_CONTENT">. Now you need &, <, >, ", and if the attribute uses single quotes, ' as well. Miss the quote encoding and an attacker can break out of the attribute with input like " onmouseover="alert(1).

3. JavaScript String Context

When user content lands inside a JavaScript string literal—like var name = 'USER_CONTENT';—HTML entities won't save you. You need JavaScript-specific encoding: escape backslashes, quotes, and line terminators. Use JSON.stringify() as a baseline, then apply HTML entity encoding on top if the string will be written to the DOM.

4. URL Context

User content in a URL—like <a href="USER_CONTENT">—needs encodeURIComponent() first, then HTML attribute encoding. Skip the URL encoding and an attacker can inject javascript:alert(1) as a protocol handler. That's a direct XSS vector.

5. CSS Context

Rare but dangerous. If user content ends up in a CSS value—like <div style="color: USER_CONTENT">—you need CSS-specific encoding (escape backslashes, parentheses, and semicolons). In practice, just don't put user content in CSS contexts. Use classes and data attributes instead.

Common Entity Mistakes That Create Security Holes

Let's look at the specific mistakes I've seen in production code—real vulnerabilities that slipped past code review because the developer thought they'd handled entities correctly.

Mistake #1: Trusting Your Framework's Auto-Escaping

Modern frameworks like React and Vue auto-escape content by default. That's great—until you use dangerouslySetInnerHTML in React or v-html in Vue. These directives bypass auto-escaping entirely. I've audited codebases where 100% of XSS vulnerabilities came from these two directives being used to render "trusted" CMS content that turned out to be user-editable.

The rule: if you're using dangerouslySetInnerHTML or v-html, you'd better have a sanitization layer (like DOMPurify) in front of it. HTML entity encoding alone isn't enough here because the browser will decode entities before parsing the HTML.

Mistake #2: Encoding in the Wrong Order

When content passes through multiple contexts—say, a URL parameter that gets inserted into an HTML attribute—order matters. You must URL-encode first, then HTML-encode. Reverse the order and the URL encoding will mangle your HTML entities, or vice versa. A concrete example: the string a&b=c should become a%26b%3Dc after URL encoding, then stay as-is after HTML encoding (no special HTML characters remain). If you HTML-encode first, you get a&amp;b=c, then URL encoding produces <code>a%26amp%3Bb%