HTML Entity Encoder / Decoder

Encode and decode HTML special characters

Common HTML Entities

& = &
< = &lt;
> = &gt;
" = &quot;
' = &#39;
/ = &#47;
` = &#96;
= = &#61;

Context Is Everything: Which Characters to Escape Where

Escaping is not a property of a string; it is a property of where the string lands. HTML has five structural characters — & < > " ' — but each rendering context cares about a different subset. In element content (between tags), only & and < can change the parse: < opens a tag, & starts a character reference. Inside a double-quoted attribute value the parser only ends the value at ", so you must escape & and "; single-quoted attributes need & and '. The genuinely dangerous case is the unquoted attribute value: it is terminated by space, tab, newline, and form feed, and interacts badly with / and =, so a payload as innocent-looking as 'x onmouseover=alert(1)' breaks out without using any of the five characters at all. This is why every security guide says to always quote your attributes — it reduces the escape set to two characters. Entity encoding also has hard limits. An entity-encoded string is still fully dangerous inside a <script> block, because the HTML parser does not decode character references in script data — yet your string may terminate the block early or be interpreted as JS. A javascript: URL placed in href survives entity encoding entirely, since the browser decodes entities before resolving the URL. Inline event handlers layer HTML decoding on top of JS parsing, so they need both escapes. The OWASP XSS Prevention Cheat Sheet enumerates these contexts precisely; the rule is to pick the encoder for the destination, never for the data.
<!-- element content: escape & and < -->
<p>Tickets &lt; $20 &amp; free entry</p>

<!-- double-quoted attribute: escape & and " -->
<input value="say &quot;hi&quot; &amp; wave">

<!-- UNQUOTED attribute: breakout with none of the five chars -->
<input value=x onmouseover=alert(1)>

<!-- entity encoding does NOT help here -->
<a href="javascript:alert(1)">  <!-- decoded before URL resolution -->

Named vs Numeric Entities — and the XML Trap

The WHATWG HTML Living Standard defines exactly 2,231 named character references, from workhorses like &nbsp; &amp; and &mdash; to obscura like &CounterClockwiseContourIntegral;. Any Unicode code point can also be written numerically: &#8594; in decimal and &#x2192; in hex both produce a rightwards arrow, and the hex form maps directly onto the U+2192 notation you see in Unicode charts, which makes it the easier one to verify. The trap is that those 2,231 names are an HTML-only vocabulary. XML predefines just five: &amp; &lt; &gt; &quot; &apos;. Feed &nbsp; to an XML parser — an XHTML page served as application/xhtml+xml, an RSS or Atom feed, an SVG file processed as standalone XML — and you get a fatal parse error, not a warning. The portable spelling is &#160;. Going the other direction, &apos; is defined in XML and HTML5 but was never part of HTML 4, so decade-old parsers and some email renderers show it literally; &#39; is the apostrophe that works everywhere, which is why serializers like PHP's htmlspecialchars emit &#39; rather than &apos;. When you decode by hand, remember that named references are case-sensitive: &Auml; is Ä while &auml; is ä, and &AMP; exists but &AMp; does not. A decoder that lowercases its input before the table lookup silently corrupts text.
Rightwards arrow (U+2192):
  named:   &rarr;      (HTML only)
  decimal: &#8594;
  hex:     &#x2192;    (matches U+2192)

Safe everywhere (HTML + XML/RSS/SVG):
  &amp; &lt; &gt; &quot; &#39; &#160;

Fatal parse error in XML: &nbsp; &mdash; &copy; ...

Missing Semicolons, &amp; in URLs, and Double Encoding

HTML parsers still honor a legacy list of named references written without the trailing semicolon — &amp, &lt, &copy and friends — because pre-HTML5 content depended on it. That leniency created a classic URL bug: in ?page=1&copy=2, the sequence &copy was historically decoded to ©, silently corrupting the query string. The modern spec patched this with an attribute-value exception (a legacy reference followed by = or an alphanumeric is left alone), but the lesson stuck: every raw & inside an href or src should be written as &amp;. Validators still flag bare ampersands in URLs for exactly this reason, and it costs nothing, because browsers decode &amp; back to & before making the request. Double encoding is the opposite failure. Encode &lt; a second time and you get &amp;lt;, which renders as the literal text &lt; instead of <. When you see &amp;quot; or &amp;amp; in a page, two layers each 'helpfully' escaped: typically a template engine auto-escaping output that was already encoded by hand, or HTML that was entity-encoded before being written to the database and then encoded again at render time. The golden rule that prevents the whole class: store raw text, and encode exactly once, at output time, with the encoder for the destination context. A decoder like this tool is the quickest way to diagnose how many layers deep a mangled string is — decode repeatedly until the output stops changing and count the passes.
?a=1&copy=2       legacy parsing: "a=1©=2"  (broken)
?a=1&amp;copy=2   safe: browser requests "a=1&copy=2"

"<b>"  encoded once   →  "&lt;b&gt;"          (correct)
       encoded twice  →  "&amp;lt;b&amp;gt;"  (renders "&lt;b&gt;")

With UTF-8, You Need Far Fewer Entities Than You Think

If your page declares <meta charset="utf-8"> — and in 2026 it should, since over 98% of the web is served as UTF-8 — you do not need entities for em dashes, curly quotes, arrows, CJK, or emoji. Typing — directly instead of &mdash; makes the source smaller (1–3 bytes instead of 7) and, more importantly, readable: a diff full of &#x2192; is unreviewable. The characters that still require entities fall into two groups. First, the structural five, forever. Second, characters that are invisible or indistinguishable in an editor and therefore dangerous to leave literal: the no-break space (&nbsp; — a literal U+00A0 looks identical to a normal space and will get 'fixed' by the next person's editor), the soft hyphen &shy;, the zero-width joiner &zwj; and zero-width non-joiner &zwnj; used in emoji sequences and Persian or Indic shaping, and bidi control characters. Writing these as entities is self-documentation. One caveat: if a page renders — garbage where you typed —, the file was saved or served in the wrong encoding. Entities would mask that bug, but fixing the charset is the real cure — mojibake papered over with &mdash; will resurface the moment anyone pastes non-ASCII content.

textContent, innerHTML, and When You Need a Sanitizer Instead

In the DOM, the safest encoder is the one you never call: element.textContent = userInput inserts the string as pure text — no parsing, no encoding needed, safe at any nesting depth. element.innerHTML = userInput is the canonical XSS sink: the string goes through the full HTML parser, so any markup in it executes. (setAttribute is likewise safe for values, though the attribute itself matters — setAttribute('href', 'javascript:...') is still a live URL.) Template frameworks internalized this lesson: {expression} in Svelte, JSX in React, and the double-brace interpolation in Vue all escape by default, which is why hand-encoding data before handing it to a component is a double-encoding bug, not a safety improvement. Each framework also ships an explicit escape hatch — {@html ...} in Svelte, dangerouslySetInnerHTML in React, v-html in Vue — whose deliberately awkward names mark the transfer of responsibility to you. That leads to the last distinction: encoding and sanitizing solve different problems. Encoding turns markup into visible text — correct when users type HTML and should see it displayed as-is. Sanitizing (DOMPurify, or the emerging native Sanitizer API) parses the markup and keeps an allowlist of safe tags while stripping script vectors — correct when users legitimately author rich text that must render as markup. Encoding a comment field and sanitizing a WYSIWYG post body are both right; swap them and you either mangle the display or open a hole.
// safe: never parsed as markup
el.textContent = userInput;

// XSS sink: parsed and executed
el.innerHTML = userInput;

// frameworks escape by default; escape hatches opt out
// Svelte:  {comment.text}   vs  {@html post.trustedHtml}
// React:   {comment.text}   vs  dangerouslySetInnerHTML
// Vue:     interpolation    vs  v-html

// rich text: sanitize, don't encode
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(userHtml);
Last updated:

About this tool

An HTML entity encoder converts characters that have special meaning in HTML — like <, >, &, and " — into safe entity references such as &lt;, &gt;, &amp;, and &quot;. This is the foundation of XSS prevention: any user-supplied content rendered into a page must be entity-encoded so the browser treats it as text instead of markup.

How to use

  1. Pick Encode to convert raw characters into HTML entities, or Decode to do the reverse.
  2. Paste the text or HTML snippet into the input box.
  3. Read the encoded or decoded output on the right.
  4. Click Copy to grab the result for use in your template, email, or doc.
  5. Use the entity reference card at the bottom as a quick lookup for common characters.

Common use cases

  • Sanitising user-generated content before injecting it into a server-rendered page.
  • Embedding code snippets that contain < and > inside a Markdown or HTML document.
  • Preparing email HTML so apostrophes and ampersands render correctly across clients.
  • Decoding scraped HTML where entities like &amp;quot; were double-encoded.
  • Verifying that an output escapes correctly before flagging an XSS bug.
  • Showing literal HTML markup in a tutorial or technical doc.

Frequently asked questions

Q. Do I still need to encode if I use a templating engine?

A. Most modern engines (Svelte, React, Jinja, ERB) auto-escape by default. But once you reach for a "raw" or "unsafe" helper you take responsibility for encoding yourself.

Q. What is the difference between &amp;#39; and &amp;apos;?

A. Both encode an apostrophe. &apos; is technically XHTML/XML; some legacy HTML parsers do not recognise it, so &#39; is the safer choice for HTML output.

Q. Does encoding fix all XSS issues?

A. No. Context matters — JavaScript strings, URL attributes, and CSS each need their own escaping. Use a battle-tested sanitizer for HTML you want to render with markup intact.

Q. Why does decoding a non-encoded string return it unchanged?

A. There is nothing to decode. Decoding only swaps recognised entities back to characters; raw text passes through untouched.