HTML entity encode/decode · Guide
HTML Entities: Complete List with Named and Numeric Codes
HTML defines more than two thousand named entities. You will use about thirty, and only five of them are genuinely required. This reference groups the ones that appear in real markup by what you are trying to achieve, gives the named, decimal and hexadecimal form of each, and covers the part most lists skip: escaping correctly in code, and why the right answer depends on where the text is going.
The five that actually matter
These characters carry meaning in HTML. Left raw in the wrong place they either break the markup or open an XSS hole, and they are the only ones you are ever obliged to escape.
& & & &
< < < <
> > > >
" " " "
' ' ' (' is HTML5 only)' is the odd one out. It was added in HTML5 and does not exist in HTML4 or in XHTML served as HTML, so ' is the safer choice anywhere the doctype is not fully under your control — including email templates, which remain the least forgiving environment for this.
If you ever escape by hand, order matters: replace & first. Escape it last and you re-escape the ampersands you just introduced, turning < into &lt; and printing the entity itself instead of the character.
Named, decimal and hexadecimal
Every character has three interchangeable written forms. Named codes such as → are readable but only exist for characters that were given a name. Numeric codes work for every character in Unicode: decimal → and hexadecimal → both produce →.
Hex is worth preferring for numeric references because Unicode code points are published in hex — U+2192 maps to → with no arithmetic. Decimal requires converting, which is where transcription errors creep in.
The trailing semicolon is not optional. Browsers will often recover from & without it, but the behaviour differs between parsers and breaks entirely in XML and XHTML. Always close the entity.
Arrows
← ← ← ⇐ ⇐ ⇐
→ → → ⇒ ⇒ ⇒
↑ ↑ ↑ ⇑ ⇑ ⇑
↓ ↓ ↓ ⇓ ⇓ ⇓
↔ ↔ ↔ ⇔ ⇔ ⇔
↵ ↵ ↵ ↗ ↗ ↗The lowercase names give single arrows and the capitalised ones give double arrows — ← against ⇐. It is a naming convention that runs through the whole entity table, so it is worth internalising once.
Maths and logic
× × × ≠ ≠ ≠
÷ ÷ ÷ ≤ ≤ ≤
± ± ± ≥ ≥ ≥
− − − ≈ ≈ ≈
√ √ √ ≡ ≡ ≡
∞ ∞ ∞ ∑ ∑ ∑
½ ½ ½ ∏ ∏ ∏
¼ ¼ ¼ ∂ ∂ ∂− is not the same character as a hyphen. The hyphen on your keyboard is U+002D; a true minus sign is U+2212 and aligns with the digits in most fonts. It matters in typeset maths and almost nowhere else.
Currency, legal and punctuation
© © © € € €
® ® ® £ £ £
™ ™ ™ ¥ ¥ ¥
§ § § ¢ ¢ ¢
¶ ¶ ¶ ° ° °
† † † ‰ ‰ ‰
• • • … … …
« « « » » »
“ “ “ ” ” ”
‘ ‘ ‘ ’ ’ ’’ doubles as the correct typographic apostrophe: it is don’t, not don't, in prose. Since it carries no structural meaning in HTML it never needs escaping for safety — the entity is purely a typographic choice.
Spaces and dashes
This group is the practical reason entities still exist. These characters are invisible or easy to confuse in source, so writing them as entities makes the intent explicit to the next person reading the file.
non-breaking space  
  thin space  
  en space  
  em space  
­ soft hyphen ­
– – en dash –
— — em dash —
‌ zero-width non-joiner ‌
‍ zero-width joiner ‍ prevents a line break between two words, which is what you want in 10 kg or Fig. 3. It is not a layout tool: strings of them to create indentation are a long-standing bad habit that CSS margin or padding does properly.
The dashes have distinct jobs. – marks ranges — 2020–2024 — — sets off a parenthetical break, and a plain hyphen joins compound words. ­ is invisible until the word needs to wrap, at which point the browser may break there and show a hyphen.
Escaping in code, not by hand
Hand-written replacement chains are where escaping bugs come from. Every language has a correct built-in, and the flags matter as much as the function.
// JavaScript — no built-in; the DOM does it correctly
const escapeHtml = (s) =>
s.replace(/[&<>"']/g, (c) => ({
'&': '&', '<': '<', '>': '>',
'"': '"', "'": ''',
}[c]));
// Better still: never build HTML from strings
el.textContent = untrusted; // safe
el.innerHTML = untrusted; // XSS// PHP — always pass the flags and the charset
htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
// htmlentities() converts every accented character too,
// which is unnecessary on a UTF-8 page and hurts readability.# Python
import html
html.escape(untrusted) # quote=True by default
html.unescape('<b>') # '<b>'The PHP flags deserve attention. Without ENT_QUOTES single quotes pass through unescaped, so a value interpolated into a single-quoted attribute can break out of it. Without ENT_SUBSTITUTE invalid UTF-8 makes the function return an empty string, which silently blanks the field rather than failing loudly.
In any modern template engine — Blade, Twig, JSX, Vue — output is escaped by default. The vulnerabilities appear where you opt out: {!! !!}, |raw, dangerouslySetInnerHTML, v-html. Those are the lines worth grepping for in a review.
Context decides the escaping
HTML escaping is not universal escaping. The same string needs different treatment depending on where it lands, and using the wrong one is a common source of bugs that look like escaping working.
text between tags htmlspecialchars <b>
attribute value same, plus quotes " '
URL parameter encodeURIComponent %3Cb%3E
inside <script> JSON.stringify "\u003cb\u003e
CSS value CSS.escapeEscaping a URL with HTML entities produces & inside a query string, which many servers then read as a literal parameter name — the classic cause of ?a=1&b=2 arriving as one broken parameter. Conversely, percent-encoding text meant for display shows %20 to the user.
Unquoted attributes deserve one warning. <div class={value}> without quotes can be escaped by the book and still be exploitable, because a space in the value ends the attribute and starts a new one. Quote every attribute; it costs nothing.
Do you still need entities?
With <meta charset="utf-8"> declared — which every page should have — you can type →, © and é directly into the source and they will render. That is more readable than →, © and é, and it is the right default for content.
So the working rule has three parts. Always escape the five structural characters, and do it with a library rather than by hand. Use entities for invisible characters such as , ­ and the zero-width joiners, where the source would otherwise hide the intent. Type everything else literally.
The one place to stay conservative is email. Client support for character sets is still inconsistent, and named entities for symbols remain the safer choice in HTML mail even though they are unnecessary on the web.
Frequently asked questions
How many HTML entities are there?
The HTML5 specification names more than 2,200, including many duplicates with and without the trailing semicolon. In practice a working set is about thirty: the five structural characters, the space and dash family, and a handful of arrows, currency and legal symbols.
Should I use ' or ' for an apostrophe?
' is safer. ' was only introduced in HTML5 and is not recognised in HTML4 or in XHTML served as HTML, so it can render literally in older parsers and in many email clients. The numeric reference works everywhere.
Do I need entities if my page is UTF-8?
Not for ordinary characters. With a UTF-8 charset declared you can type →, © and é directly. You still must escape &, <, >, " and ' in any untrusted value, and entities remain useful for invisible characters like where the source would otherwise be ambiguous.
What is the difference between htmlspecialchars and htmlentities in PHP?
htmlspecialchars() converts only the five characters that matter for markup. htmlentities() converts every character that has a named entity, including accented letters, which is unnecessary on a UTF-8 page and makes the output harder to read. Prefer htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8').
Why does my ampersand show as &amp; on the page?
The text was escaped twice — typically once in application code and again by a template engine that escapes by default. Find the layer doing it manually and remove it; escaping belongs at output, once.
Is the right way to add spacing?
No. It exists to prevent a line break between two words, as in 10 kg. Runs of used for indentation or gaps are a workaround for missing CSS, and they behave unpredictably across font sizes and screen widths. Use margin or padding.
Ready to try it?
Open the free browser-based HTML entity encode/decode and apply what you just read — no sign-up, runs locally.
Open the HTML entity encode/decode tool