Guide · Security

How to Encode HTML Entities and Avoid XSS Pitfalls

Updated 2026-08-09 · 5 min read

HTML entity encoding replaces characters that have meaning in markup with named or numeric stand-ins. < becomes &lt;. The browser shows a less-than sign instead of starting a tag. That is the correct transform when you want to display a string that happens to look like HTML.

It is not a complete XSS defense. Cross-site scripting is a context problem: the same bytes are dangerous in a <script> block, an onclick attribute, or a href that starts with javascript:, even when they would be harmless in a paragraph. A paste-box that encodes & < > " ' is a helper for text nodes, not an application security program.

DevOkk’s HTML Encoder runs in the browser so you can encode or decode a snippet without uploading a template that contains customer text.

What entity encoding actually does

HTML treats &, <, and > as markup. If a user types <b>hello</b> and you concatenate it into innerHTML, you have just added a tag. If you insert &lt;b&gt;hello&lt;/b&gt; as text, the user sees the angle brackets.

The important entities for safety in HTML text:

CharacterEntityWhy
&&amp;Must be first; otherwise you double-encode or break other entities
<&lt;Starts a tag
>&gt;Ends a tag (encode it anyway)

In quoted attributes, also encode " as &quot; or use a quoting style you enforce. In single-quoted attributes, encode '. If you put untrusted data into an unquoted attribute, you have already lost; encoding will not save a space-delimited attribute parser.

Named entities for ©, , and so on are convenience. They do not make a page safer. Numeric entities (&#39;) are equivalent to named ones for the same code point.

Encode & first when you do this by hand. Encode & last when you decode. Order mistakes are how &lt; becomes &amp;lt; and a week of “why is this showing raw entities.”

Why this is not a complete XSS defense

XSS happens when an attacker’s string is interpreted as code in the victim’s browser. Entity encoding of a text node stops the classic <script> in a comment field. It does not stop:

JavaScript contexts. const x = "USER"; - a " or </script> in USER breaks out. You need JS string escaping or, better, no interpolation into scripts at all. JSON in a <script type="application/json"> with correct serialization is the usual fix.

Event-handler attributes. onclick="...". Untrusted data should never land here. Encoding < is irrelevant if the attacker can close the quote and add another attribute.

URL contexts. href and src. A value of javascript:alert(1) has no <. Entity encoding does not neutralize it. Allow-list https: and relative paths.

CSS contexts. style="...". Expressions and url() are their own mess. Do not interpolate untrusted CSS.

DOM APIs. innerHTML, document.write, insertAdjacentHTML interpret strings as HTML. textContent and setAttribute (with a safe name) do not. Prefer the safe APIs. Encoding then assigning to innerHTML is backwards: you wanted text, so use textContent.

Sanitization vs encoding. If the product requirement is “users may submit a subset of HTML,” you need a sanitizer with an allow-list (and a policy for links and images). Encoding the whole submission turns their <em> into visible tags. That is a different product.

Frameworks (React {} children, Vue text interpolation, the well-lit path in each template language) encode for HTML text by default. The bugs are the escape hatches: dangerouslySetInnerHTML, v-html, raw _html in emails.

Encoding a snippet locally

Use the tool when you are preparing a static example, checking what a CMS stored, or decoding a feed that arrived already escaped.

  1. Open HTML Encoder. No account.
  2. Paste the snippet. Encode to see the safe-for-text-node form. Decode if you received entities and need the original characters for a JSON fixture or a regex test.
  3. Decode once. If you still see &amp;, the source was double-escaped. Decide which layer was wrong instead of decoding until it “looks right,” or you will recreate a tag.
  4. If the string is actually a query parameter (%3Cscript%3E), decode URLs with URL Decoder first. Then decide whether the next consumer is HTML or something else.

This is inspection. Shipping software should encode at the boundary where the string enters HTML, every time, in code you can grep.

Context first, then characters

Ask where the string will sit:

  • Text between tags. Entity-encode & < >. Framework default.
  • Quoted attribute. Also quotes. Prefer setAttribute / JSX attributes.
  • URL attribute. Validate scheme, then encode for URLs, not just HTML.
  • JavaScript. Do not concatenate. Pass data through json_encode / JSON.stringify into a safe sink.
  • Email HTML. Many clients are worse than browsers. Encode, and still avoid embedding untrusted HTML.

If you cannot name the context, do not invent an encoder chain. Put the string in textContent and stop.

Regex is a poor XSS filter. You will miss an encoding variant. If you are testing a pattern for something else, that is a different tool; do not build a sanitizer out of a regex playground.

What a local encoder will not do

It will not review your React tree. It will not find innerHTML in a dependency. It will not make javascript: safe.

It will not replace Content Security Policy. CSP is a backstop, not a reason to skip encoding.

It will not make Base64 or URL encoding into HTML safety. Those transforms are covered in their own guides. Mixing them at random (encode URL then encode HTML then hope) is how double-encoding bugs ship.

Very large pasted documents can be slow in a textarea. That is a browser limit, not a prompt to upload the template to a converter.

Encode the snippet, then keep escaping in the template

If you need to see encoded or decoded markup in the next minute, open HTML Encoder. If the string is a query value, start at URL Decoder.

For production, keep escaping in the template layer and treat dangerouslySetInnerHTML (and friends) as a reviewed exception. Entity encoding is one control for one context. XSS is the rest of the contexts.

Frequently asked questions

Does HTML entity encoding stop XSS?

It helps when you are putting untrusted text into an HTML text node. It is not a complete XSS defense. Attribute contexts, JavaScript contexts, CSS, and javascript: URLs need different escaping. Use your framework’s contextual encoding.

Which characters must become entities in HTML text?

At minimum &, <, and > (&amp;, &lt;, &gt;). Quotes (&quot;, &#39;) matter when the value sits inside an attribute. Encoding © as &copy; is readability, not security.

Is encoding the same as sanitizing HTML?

No. Encoding turns markup into visible text. Sanitizing keeps some tags and drops others. If you need rich text from users, use a maintained sanitizer and a tight allow-list, not entity encoding of the whole string.

When should I decode entities?

When you received text that was already escaped (&amp;lt;) and you need the original characters for a non-HTML consumer. Decode once. Double-decoded markup can become live HTML again.

Should I encode a string in the browser before I ship it?

A local tool is useful to inspect or to prepare a snippet. Production escaping belongs in the template engine or UI library at render time, so you cannot forget a code path. HTML Encoder is for the tab, not for your only defense.

URL encoding vs HTML entities?

Query strings and paths use percent-encoding. HTML uses entities. Encoding < as %3C does not make it safe inside a page, and &amp; does not make a URL valid. Use URL Decoder for URLs.

More reading that links back to the same tools and workflows.

GuideSecurity

Base64 Is Not Encryption: Encoding vs Secrets

Base64 hides nothing. Why people treat it like a cipher, what it is actually for (data URLs, JWTs, APIs), and how to encode or decode in the browser without uploading a key.

7 min read