HTML Entities & XSS
HTML entity encoding is the single most effective defense against XSS in HTML contexts — but only if you use it in the right place. Here is how to think about escaping by context.
name comes from user input)<div>Hello, ${name}!</div><div>Hello, ${encodeHtml(name)}!</div>What XSS Really Is
Cross-site scripting happens when an attacker's text ends up executed as code in another user's browser. The usual vehicle is HTML:<script>fetch('/steal?c=' + document.cookie)</script>inserted into a comment or a profile field, rendered by a server that forgot to escape it.
Entity Encoding Breaks the Vehicle
Replacing < with <, > with >, & with &, and the two quote characters with " and ' turns markup into literal text. The browser renders it, but never parses it as HTML — the attack becomes a visible string instead of a running script.
Context Matters More Than You Think
HTML entities protect only HTML text and most attribute values. Different contexts need different escaping:
- HTML body / text — HTML entity encode (
< > & " ') - HTML attribute (quoted) — HTML entity encode; always quote your attributes
- URL in href / src — URL-encode the value and validate the scheme (block
javascript:) - Inside
<script>— JavaScript string escape (\u00XX) or render data as JSON - Inline CSS — allow a strict whitelist only; never substitute untrusted strings
- Inside
<style>— same as inline CSS; prefer separate stylesheets
Templates Already Do This — Mostly
React, Vue, Svelte, Angular, Handlebars, Jinja2 (with autoescape), Mustache ({{name}}), and ERB (<%= h name %>) all HTML-escape text interpolations by default. The holes are:
- React's
dangerouslySetInnerHTML, Vue'sv-html, Svelte's{@html} - Writing to
element.innerHTMLdirectly in JavaScript - Attribute values interpolated without quoting
href="${url}"without scheme validation (allowsjavascript:)- Templates that disable autoescape (don't)
Content Security Policy Buys You Depth
Even with perfect escaping, a strict CSP (script-src 'self', no 'unsafe-inline') limits the damage from any bug that slips through. Think of entity encoding as the fence and CSP as the moat; use both.
Common Mistakes
- Encoding on input instead of on output — same string ends up in HTML, JSON, logs, and the DB, all with different escaping needs
- Double-encoding — the browser already decodes once, so
&amp;renders as&instead of& - Trusting a sanitizer to strip "bad" tags; keep an allowlist, don't guess what is bad
- Thinking Base64 is encoding for safety — it isn't. See Base64 explained.
Try It
Encode or decode HTML entities instantly with our free HTML Entity Encoder. For URLs, use the URL Encoder.
Encode on output
Not on input — same string lives in HTML, JSON, logs with different needs.
Context-specific
HTML, attribute, URL, JS, CSS all need different escaping.
CSP as depth
Strict Content-Security-Policy limits any slip-ups.