HTML Entity Encoding: Preventing XSS and Special Character Issues

1 9 115
calendar_todayschedule1 min read

HTML entity encoding is one of the fundamental security practices in web development. It also solves practical problems with displaying special characters correctly. Here's a complete reference.

What is HTML entity encoding?

HTML entities are textual representations of characters that have special meaning in HTML. The most critical ones:

| Character | Encoded | Reason |

|-----------|---------|--------|

| <</code> | < | Start of HTML tag |

| > | > | End of HTML tag |

| & | & | Start of entity reference |

| " | " | Attribute value delimiter |

| ' | ' | Attribute value delimiter |

If these characters appear unencoded in HTML content, they break the HTML structure and create XSS vulnerabilities.

A HTML entity encoder converts raw text (including any special characters or user input) into safe HTML in one step.

Why this matters: XSS

Cross-Site Scripting (XSS) occurs when attacker-controlled content is inserted into HTML without encoding. Example:

Vulnerable code (PHP):

`<code>php</p> <p>echo "Hello, " . $_GET['name'] . "!";</p> </code>

If name=alert('XSS'), the output is:

<code>html <p>Hello, <script>alert('XSS')</script>!</p> </code>

The script runs in the visitor's browser.

Fixed code:
<code>php <p>echo "Hello, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8') . "!";</p> </code>

Output: Hello, <script>alert('XSS')</script>!

The browser displays the text but doesn't execute it.

HTML encoding by context

Different locations in HTML require different encoding:

HTML content (between tags):
<code> <p>Encode: < > & " '</p> <p>Use: htmlspecialchars() or equivalent</p> </code>

HTML attribute values:
`html

Must encode: < > & " and ' if inside single-quoted attribute

` JavaScript strings inside HTML: `html var name = "<?= json_encode($name) ?>"; ` Use json_encode() not htmlspecialchars() for JavaScript context — the escaping rules differ. URL attributes: `html ">

Must URL-encode the value, then HTML-encode the attribute.

` The dangerous anti-pattern: `html <!-- WRONG: double-encoding --> ← displays literal text, not a link <!-- WRONG: encoding only the outer context --> var x = "<?= htmlspecialchars($input) ?>"

← HTML encoding isn't correct for JS string context

` PHP `php // HTML content and attribute values htmlspecialchars($str, ENT_QUOTES, 'UTF-8') // Encodes: < > & " ' // ENT_QUOTES ensures both quote types are encoded // All characters with named HTML entities htmlentities($str, ENT_QUOTES, 'UTF-8') // Decode html_entity_decode($str, ENT_QUOTES, 'UTF-8') htmlspecialchars_decode($str, ENT_QUOTES) ` JavaScript React automatically encodes JSX content: `jsx // Safe — React encodes automatically return {userInput}
; ` DOM manipulation: `javascript // Safe: textContent doesn't parse HTML element.textContent = userInput; // UNSAFE: innerHTML parses HTML element.innerHTML = userInput; // XSS if input contains // Safe with encoding: element.innerHTML = userInput .replace(/&/g, '&') .replace(/ .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); // Or use DOMPurify for rich content that includes some HTML: import DOMPurify from 'dompurify'; element.innerHTML = DOMPurify.sanitize(richContent); ` Template literals:
`javascript
// UNSAFE: direct interpolation into HTML template
const html =
${userInput}
; // SAFE: encode first function escapeHtml(str) { return str .replace(/&/g, '&') .replace(/ .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } const html =
${escapeHtml(userInput)}
; ` Python `python
import html Encode (escapes &, <, >, ", ') escaped = html.escape(user_input, quote=True) Decode decoded = html.unescape('<script>') Django templates: auto-escape by default {{ user_input }} # auto-escaped {{ user_input|safe }} # bypass escaping — use only for trusted content! Jinja2: auto-escape when configured {{ user_input }} # auto-escaped (if autoescape=True) {{ user_input|safe }} # bypass
` Common HTML entities for typography Beyond security, entities are used for special typography: | Entity | Output | Use | |--------|--------|-----| |   | (non-breaking space) | Prevent line breaks | | | — | Em dash | | | – | En dash | | | … | Ellipsis | | © | © | Copyright | | ® | ® | Registered trademark | | | ™ | Trademark | | | € | Euro sign | | £ | £ | British pound | Named entities are more readable than numeric equivalents (©) for common symbols. Numeric entities When no named entity exists, use numeric form: Decimal: A (= 'A', decimal Unicode code point)Hex: A (= 'A', hex Unicode code point) These work for any Unicode character, not just the ones with named entities. Content Security Policy: the other layer HTML encoding prevents XSS in reflected and stored contexts. Content Security Policy (CSP) adds another defensive layer by restricting what scripts can run: <code> <p>Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com;</p> </code> With this header, even if a tag gets injected (encoding failure), the browser won't execute it because the source doesn't match the policy. HTML encoding and CSP are complementary — use both. Always encode user-provided content before inserting it into HTML. Frameworks like React, Django, and Rails handle this automatically for templated output, but manual DOM manipulation and string concatenation require explicit encoding. The five characters < > & " '` are the critical ones — get those right and you prevent the most common XSS patterns.

Originally published at https://snappytools.app/html-entity-encoder/

1 Comment

0 votes
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Comparison: Universal Import vs. Plaid/Yodlee

Pocket Portfolio - Mar 12

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

The Interface of Uncertainty: Designing Human-in-the-Loop

Pocket Portfolio - Mar 10

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelskiverified - Apr 23

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1
chevron_left
2.4k Points125 Badges
101Posts
0Comments
SnappyTools builds free, fast, browser-based tools for developers, writers, and designers. No signup... Show more

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!