2026-02-28•3 min read•by DevUtilz
HTML Entity Encoding and Escaping
HTMLSecurityXSSTutorial
HTML Entity Encoding and Escaping
HTML escaping is your primary defense against XSS attacks. Learn when and how to use it correctly.
Why Escape HTML?
HTML has special characters that change how content is interpreted. Without escaping:
<script>becomes executable codeonclickbecomes an event handler<img onerror=alert(1)>runs JavaScript
Common HTML Entities
| Character | Entity | Description |
|-----------|--------|-------------|
| < | < | Less than |
| > | > | Greater than |
| & | & | Ampersand |
| " | " | Double quote |
| ' | ' | Single quote |
| | | Non-breaking space |
JavaScript Implementation
Escape Function
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, char => map[char]);
}
// Usage
const userInput = '<script>alert("xss")</script>';
const safe = escapeHtml(userInput);
// Result: <script>alert("xss")</script>
Context-Aware Escaping
function escapeHtmlAttribute(str) {
return str.replace(/[&<>"']/g, char => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
})[char]);
}
function escapeHtmlUrl(str) {
return encodeURIComponent(str);
}
Framework Solutions
React (Automatic)
React escapes by default:
const userInput = '<script>alert(1)</script>';
return <div>{userInput}</div>;
// Output: <script>alert(1)</script>
Vue (v-text directive)
<!-- Automatically escapes -->
<span v-text="userInput"></span>
<!-- Raw HTML - dangerous! -->
<span v-html="userInput"></span>
Angular (Sanitization)
import { DomSanitizer } from '@angular/platform-browser';
constructor(private sanitizer: DomSanitizer) {}
safeHtml = this.sanitizer.bypassSecurityTrustHtml(userInput);
Where to Escape
1. User Input Display
// Always escape user-generated content
comment.textContent = userInput;
2. Dynamic URLs
// Encode URL parameters
const url = `https://example.com/search?q=${encodeURIComponent(query)}`;
3. JSON Data in HTML
// Escape when embedding JSON
const json = JSON.stringify(data).replace(/</g, '\\u003c');
Complete Example
function sanitizeUserInput(input) {
if (!input || typeof input !== 'string') {
return '';
}
return input
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\n/g, '<br>');
}
Common Mistakes
1. Double Encoding
// Wrong - double encoding
const bad = escapeHtml(escapeHtml('<div>'));
// Output: &lt;div&gt;
// Correct - escape once
const good = escapeHtml('<div>');
// Output: <div>
2. Wrong Context
// Dangerous - unsanitized input in onclick
const bad = `<button onclick="alert('${userInput}')">Click</button>`;
// Safe - use data attributes
const safe = `<button data-message="${escapeHtml(userInput)}">Click</button>`;
3. Forgetting to Escape URLs
// Dangerous
const bad = `<a href="${userUrl}">Link</a>`;
// Safe
const safe = `<a href="${encodeURI(userUrl)}">Link</a>`;
Testing for XSS
const testCases = [
'<script>alert(1)</script>',
'<img src=x onerror=alert(1)>',
'<svg onload=alert(1)>',
'javascript:alert(1)',
'<a href="javascript:alert(1)">'
];
testCases.forEach(test => {
const escaped = escapeHtml(test);
console.log(`Input: ${test}`);
console.log(`Escaped: ${escaped}`);
});
Conclusion
Always escape user input before displaying it. Use your framework's built-in sanitization when available. Never trust user-provided HTML without proper sanitization.