What Are HTML Entities?
HTML entities are special text sequences used to represent characters that either have reserved meaning in HTML or cannot be easily typed or transmitted. An entity begins with an ampersand (&) and ends with a semicolon (;). Between those delimiters is either a descriptive name (a named entity like &) or a numeric code point (a numeric entity like & or &).
At first glance, entities seem like a minor typographic detail. In reality, they are a cornerstone of web security, internationalisation, and reliable browser rendering. Every web developer who works with dynamic content — user input, CMS data, email templates, or templating engines — must understand how and when to encode HTML entities.
A Brief History of HTML Entities and Character Encoding
The ASCII Era (1960s–1980s)
The American Standard Code for Information Interchange (ASCII) defined 128 characters: the 26 English letters (upper and lower case), digits, punctuation, and control codes. This was sufficient for American English but completely inadequate for the rest of the world's languages.
Latin-1 / ISO-8859-1 (1980s–1990s)
ISO-8859-1 (also called Latin-1) extended ASCII to 256 characters by using the 8th bit, adding accented characters used in Western European languages (é, ü, ñ, etc.). HTML 2.0 and HTML 3.2 formally adopted Latin-1 as their reference character set, and HTML named entities for many of these characters were defined at this time — entities like é (é), ü (ü), and ñ (ñ).
The problem: 256 characters still couldn't cover Japanese, Arabic, Chinese, Korean, or hundreds of other scripts. Different regions invented incompatible encodings (Shift-JIS, Big5, KOI8-R…), creating the "Mojibake" problem — garbled text when encodings were mixed up.
Unicode and UTF-8 (1991–Present)
The Unicode Consortium published its first standard in 1991 with the goal of assigning a unique code point to every character in every writing system. Today, Unicode covers over 140,000 characters across more than 150 scripts.
UTF-8, introduced in 1992 by Ken Thompson and Rob Pike, encodes Unicode code points as 1–4 bytes and is backward compatible with ASCII. It became the dominant encoding for the web in the 2000s. As of 2024, over 98% of web pages use UTF-8.
Why Do Entities Still Matter in a UTF-8 World?
If we can encode any character with UTF-8, why do entities still exist? Three reasons:
- Reserved characters:
<,>, and&have special meaning in HTML markup. Even in UTF-8 documents, you must escape them to display them literally. - Attribute delimiters:
"and'delimit attribute values and must be escaped within those values. - Whitespace control:
(non-breaking space) controls layout in ways that regular spaces cannot.
Core Concepts: How HTML Entities Work
Named Entities
Named entities are the most human-readable form, using a mnemonic name derived from the character's description. HTML5 defines over 2,000 named entities.
<!-- Using named entities -->
<p>Bread & Butter</p> <!-- displays: Bread & Butter -->
<p>3 < 5 and 10 > 7</p> <!-- displays: 3 < 5 and 10 > 7 -->
<p>Copyright © 2026</p> <!-- displays: Copyright © 2026 -->
<p>Price: 49€</p> <!-- displays: Price: 49€ -->
Numeric Entities: Decimal and Hexadecimal
Any Unicode character can be referenced by its code point in either decimal or hexadecimal form:
- Decimal:
&#followed by the decimal code point — e.g.,<for<(U+003C) - Hexadecimal:
ollowed by the hex code point — e.g.,<for<
Both forms are equivalent. Hexadecimal is common in technical documentation because Unicode code points are normally expressed in hex (U+003C).
<!-- All three are equivalent ways to display < -->
<
<
<
The 5 Critical Security Entities
These five characters form the foundation of HTML injection defence:
| Character | Named Entity | Decimal | Hex | Context |
|---|---|---|---|---|
< |
< |
< |
< |
Opens HTML tags |
> |
> |
> |
> |
Closes HTML tags |
& |
& |
& |
& |
Starts entities |
" |
" |
" |
" |
Double-quoted attributes |
' |
' |
' |
' |
Single-quoted attributes |
Always encode all five when reflecting user input into HTML.
HTML Entity Reference Table
| Character | Named Entity | Decimal | Hex | Usage |
|---|---|---|---|---|
< |
< |
< |
< |
Tag delimiters |
> |
> |
> |
> |
Tag delimiters |
& |
& |
& |
& |
Entity prefix |
" |
" |
" |
" |
Attribute values |
' |
' |
' |
' |
Attribute values |
|
|
  |
  |
Non-breaking space |
© |
© |
© |
© |
Copyright |
® |
® |
® |
® |
Registered trademark |
™ |
™ |
™ |
™ |
Trademark |
€ |
€ |
€ |
€ |
Euro sign |
— |
— |
— |
— |
Em dash |
– |
– |
– |
– |
En dash |
XSS Prevention: Why Encoding Saves Your Site
Cross-Site Scripting (XSS) is one of the most prevalent web security vulnerabilities. It occurs when an attacker injects malicious scripts into content that is then served to other users. HTML entity encoding is the primary defence.
The Classic XSS Attack
Consider a search feature that echoes back the user's query:
<!-- VULNERABLE: directly inserting user input -->
<p>You searched for: <?php echo $_GET['q']; ?></p>
An attacker crafts a URL like:
https://example.com/search?q=<script>document.cookie</script>
The browser renders the <script> tag and executes the attacker's code. With document.cookie, they steal session tokens. With fetch(), they exfiltrate data to an attacker-controlled server.
The Fix: Encode on Output
<!-- SAFE: encode all output -->
<p>You searched for: <?php echo htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8'); ?></p>
Now the browser sees:
<p>You searched for: <script>document.cookie</script></p>
The script is displayed as harmless text — no execution occurs.
Practical Code Examples
JavaScript: Safe DOM Manipulation
The safest way to insert user-generated content in JavaScript is textContent, which never interprets HTML:
// SAFE: textContent never parses HTML
const el = document.getElementById('output');
el.textContent = userInput; // automatically escapes everything
// DANGEROUS: innerHTML parses and executes HTML
el.innerHTML = userInput; // NEVER do this with untrusted input
If you must build HTML strings in JavaScript, always escape first:
function escapeHtml(str) {
return str
.replace(/&/g, '&') // must come first
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const safe = `<p>You searched for: ${escapeHtml(userInput)}</p>`;
Note: always escape & first — if you escape < first, the & in < would itself get escaped into &lt;, causing double encoding.
PHP: htmlspecialchars() and htmlentities()
PHP provides two primary functions for HTML encoding:
// htmlspecialchars: encodes only the 5 critical characters
$safe = htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// htmlentities: encodes ALL characters with named entity equivalents
$safe = htmlentities($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
Key difference: htmlspecialchars() only encodes <, >, &, ", and '. htmlentities() also encodes accented letters and symbols like é → é. For UTF-8 documents, htmlspecialchars() is usually preferred — UTF-8 can represent all characters directly; only the dangerous five need escaping.
Always pass ENT_QUOTES to encode both quote types, and always specify 'UTF-8' as the charset.
Python: html.escape()
import html
# Basic escaping
safe = html.escape(user_input)
# Escape single quotes too (quote=True is default since Python 3.2)
safe = html.escape(user_input, quote=True)
# Example
user_input = '<script>alert("XSS")</script>'
print(html.escape(user_input))
# Output: <script>alert("XSS")</script>
HTML Templates (Jinja2, Django, Handlebars)
Most modern templating systems auto-escape by default:
<!-- Jinja2 / Django: auto-escaped by default -->
<p>{{ user_comment }}</p>
<!-- Deliberately render raw HTML (DANGEROUS if user-controlled): -->
<p>{{ user_comment | safe }}</p>
<!-- Handlebars: double braces escape, triple braces do not -->
<p>{{userComment}}</p> <!-- escaped — safe -->
<p>{{{userComment}}}</p> <!-- raw HTML — dangerous! -->
Use Cases in the Real World
1. Technical Documentation and Code Blogs
When writing about HTML, you frequently need to show code examples containing <, >, and &. Entities let you display these as literal characters without breaking the page structure:
<pre><code>
Use <div> and </div> to wrap sections.
The & character starts an HTML entity.
</code></pre>
2. CMS and User-Generated Content
Any CMS that stores and displays user-generated text must encode HTML entities before outputting to the page. This includes blog comments, forum posts, product reviews, and social media posts. Failure to do so is responsible for a huge proportion of real-world XSS incidents.
3. HTML Email Templates
Email clients are notoriously inconsistent. Using named entities for typographic characters (—, ‘, ’, …) helps ensure correct rendering across Gmail, Outlook, Apple Mail, and legacy clients like Outlook 2007 (which uses Word's rendering engine).
4. Typography and Special Symbols
Entities provide reliable access to typographic characters that are awkward to type or may not survive copy-paste across systems:
<p>The em dash—used for asides—is more expressive than a hyphen.</p>
<p>She said “hello” and smiled.</p>
<p>Price: 29 €</p>
<!-- prevents "29" and "€" from wrapping to separate lines -->
5. Internationalisation in Legacy Systems
In legacy systems that cannot reliably handle UTF-8, numeric entities allow encoding of any Unicode character:
<!-- Chinese character for "dragon" (U+9F99) as a decimal entity -->
龙
<!-- Japanese hiragana あ (U+3042) -->
あ
Named vs. Numeric Entities: Comparison
| Aspect | Named (<) |
Decimal (<) |
Hexadecimal (<) |
|---|---|---|---|
| Readability | High | Medium | Low |
| Coverage | ~2,000 chars | All Unicode | All Unicode |
| HTML5 support | Full | Full | Full |
| XML support | Only 5 predefined | Full | Full |
| Best for | Common chars | Any Unicode | Technical/Unicode refs |
HTML vs. XML: A Critical Difference
XML only predefines 5 entities (<, >, &, ", '). All other named entities like © or are undefined in XML unless declared in a DTD.
<!-- INVALID in XML (undefined entity): -->
<p>Copyright © 2026</p>
<!-- VALID in XML (numeric entity works everywhere): -->
<p>Copyright © 2026</p>
<!-- VALID in HTML5 (both work): -->
<p>Copyright © 2026</p>
If you are writing XHTML or SVG, use numeric entities for anything beyond the basic 5, or use the literal UTF-8 character directly.
Best Practices
1. Use UTF-8 Throughout Your Stack
Declare UTF-8 everywhere — the database collation, the HTTP Content-Type header, and the HTML <meta charset> tag. This eliminates the need to encode non-ASCII characters with entities.
<meta charset="UTF-8">
header('Content-Type: text/html; charset=UTF-8');
2. Encode Context-Appropriately
Different injection contexts require different escaping strategies:
- HTML body: encode
<,>,& - HTML attributes: encode
<,>,&,",' - JavaScript strings: use
\uXXXXescaping or JSON encoding - CSS values: different escaping rules apply
- URLs: use percent-encoding (
%3Cnot<)
A character encoded for one context is not necessarily safe in another.
3. Encode on Output, Not on Input
Store raw data in your database. Encode when outputting to HTML. If you encode on input, you risk double-encoding on output, and the data becomes corrupted in non-HTML contexts (JSON APIs, plain text emails, etc.).
4. Never Decode Untrusted Input Before Processing
Decoding user-supplied entities before applying security filters defeats the purpose. <script> decoded becomes <script> — a classic bypass of naive "block angle brackets" filters.
5. Avoid Double Encoding
Double encoding (&lt; which renders as <, not <) is a common mistake when multiple application layers each encode independently. Centralise your encoding in a single presentation layer.
6. Remember ' Was Not in HTML4
The entity ' is defined in XML and XHTML but was not defined in HTML4. In HTML4 environments, use ' instead. HTML5 officially added ' to the named entity list.
Frequently Asked Questions
Q: Do I need to encode every special character, or just the dangerous ones?
For security, you must encode at least the 5 critical characters (< > & " '). For typography (copyright signs, dashes, currency symbols), using the literal UTF-8 character in a UTF-8 document is perfectly fine. Entities are more critical in legacy systems or when character encoding cannot be guaranteed.
Q: What is the difference between & and &?
& is the literal ampersand character. & is its HTML entity representation. In HTML source, whenever you want to display a literal &, you must write &. If you write bare & before a word, browsers may try to interpret it as an entity start and render incorrectly.
Q: Why does behave differently from a regular space?
A regular space (U+0020) is a breaking space — browsers can break lines at it, and multiple consecutive spaces collapse to one. (non-breaking space, U+00A0) prevents line breaks between surrounding characters and does not collapse. Useful for keeping values like "100 km" or "Dr. Smith" on one line.
Q: Can I use numeric entities for emoji?
Yes. Emoji have Unicode code points and can be represented as numeric entities. The 😀 emoji (U+1F600) is 😀 in hex or 😀 in decimal. In UTF-8 documents you can paste the emoji directly, but numeric entities work as a reliable fallback.
Q: What is the XSS risk specific to href attributes?
The href attribute has a unique danger: URLs can use the javascript: protocol. HTML encoding alone is not sufficient:
<!-- DANGEROUS even though < and > are encoded: -->
<a href="javascript:alert(1)">Click me</a>
<!-- Safe: validate that href starts with http:// or https:// -->
<?php
$url = $_GET['url'];
if (!preg_match('/^https?:\/\//i', $url)) {
$url = '#'; // reject dangerous protocols
}
echo '<a href="' . htmlspecialchars($url) . '">Link</a>';
?>
Q: Is it safe to use innerHTML if I encode the content first?
If you correctly encode all 5 critical characters before assigning to innerHTML, it is generally safe for injecting plain text. However, textContent is simpler and more foolproof. Reserve innerHTML for cases where you intentionally want to insert controlled HTML structure.
Q: Do modern JavaScript frameworks handle HTML encoding automatically?
Yes — React, Vue, Angular, and Svelte all escape output by default. React's JSX escapes values interpolated with {} automatically. However, each provides an explicit bypass (React's dangerouslySetInnerHTML, Vue's v-html) that must be used with extreme care and only with trusted content.
Summary
HTML entities are an essential mechanism for:
- Security — neutralising
<,>,&,", and'to prevent XSS injection - Correctness — ensuring characters with reserved HTML meaning are displayed literally
- Compatibility — representing any Unicode character in legacy or restricted environments
- Typography — inserting em-dashes, non-breaking spaces, currency symbols, and other special characters reliably
In a modern UTF-8 stack, you primarily need to encode the 5 security-critical characters when outputting dynamic content to HTML. Named entities like and — remain useful for typography. Understanding the difference between named and numeric entities, and the divergence between HTML and XML rules, will make you a more effective and security-conscious web developer.