HTML Entity Encoding for XSS Protection

What is XSS?

Cross-Site Scripting (XSS) occurs when an attacker injects malicious scripts into web pages viewed by other users. HTML entity encoding is the first line of defense — it converts special characters into their entity equivalents so browsers treat them as text, not executable code.

<script>alert('XSS')</script>

<!-- After encoding, it renders as literal text -->
&lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;

Critical Characters to Encode

CharacterEntityNumericWhy It's Dangerous
<&lt;&#60;Opens HTML tags
>&gt;&#62;Closes HTML tags
&&amp;&#38;Starts entity references
"&quot;&#34;Breaks out of attributes
'&apos;&#39;Breaks single-quoted attributes
/&#47;&#47;Can close script tags

innerHTML vs textContent

The most common XSS vector in JavaScript is using innerHTML with untrusted input. textContent is always safe because it never parses HTML.

// DANGEROUS: renders HTML, allows XSS
const userInput = '<img src=x onerror=alert(1)>';
element.innerHTML = userInput; // executes script!

// SAFE: treats everything as plain text
element.textContent = userInput; // displays literal text

// Manual encoding for innerHTML
function escapeHtml(str) {
  const map = { '&': '&', '<': '<', '>': '>',
                '"': '"', "'": ''' };
  return str.replace(/[&<>"']/g, c => map[c]);
}

element.innerHTML = escapeHtml(userInput); // safe

Rule: Never use innerHTML with user input unless you encode it first. Use textContent for plain text. If you must render HTML, sanitize it with a library like DOMPurify.

Framework Auto-Encoding

Modern frameworks encode output automatically. React, Vue, and Angular all escape interpolation by default, but certain patterns bypass this protection.

// React: safe by default
function Comment({ text }) {
  return <div>{text}</div> // auto-encoded
}

// React: DANGEROUS - bypasses encoding
function UnsafeComment({ html }) {
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

// Vue: safe by default
<template>
  <div>{{ userText }}</div> <!-- auto-encoded -->
</template>

// Vue: DANGEROUS - raw HTML
<div v-html="userText"></div>

// Server-side: always encode before template insertion
// Python
from markupsafe import escape
safe_name = escape(user_input)

// Node.js
const safe = require('html-entities').encode(userInput);

Need to encode or decode HTML entities?

Try HTML Entity Encoder/Decoder