HTML Escaping: Stop Markup Bugs Before They Become XSS
Escape HTML safely by separating text, attributes, URLs, and trusted markup before user content reaches a page or email.

HTML escaping is easy to remember and easy to apply in the wrong place. Escaping user text for a paragraph is not the same as escaping an attribute, a URL, a JSON blob inside a script tag, or rich text that allows a small set of tags.
A practical workflow starts by identifying context. Then you encode for that exact context once, as late as possible, before content is rendered.
Know the context before escaping
The same input can be safe in one place and dangerous in another. Treat every output location as its own rule set.
- Text nodes: Escape
&,<, and>so user text cannot become markup. - HTML attributes: Escape quotes as well, then validate whether the attribute should accept arbitrary text at all.
- URLs: Encode URL parts and reject dangerous protocols before writing to
hreforsrc. - Inline JavaScript or CSS: Avoid placing user content here. These contexts need different encoders and are easy to get wrong.
- Rich text: Sanitize against an allowlist. Escaping alone is not enough if you intentionally allow tags.
Prefer framework escaping, then handle the edges
React, Vue, Svelte, and most template engines escape normal text bindings by default. Keep that default path for ordinary user content. Bugs usually appear when code reaches for raw HTML APIs, builds strings by hand, or decodes content before rendering.
- Render plain user text through normal template bindings whenever possible.
- Use raw HTML only for content that has already passed through a trusted sanitizer.
- Do not decode stored content just because it looks escaped in the database.
- Keep escaping near the output boundary so you do not double-escape values while moving them through the system.
For quick inspection, paste suspicious snippets into the HTML Escape / Unescape tool. If the issue is inside a query string or redirect URL, use the URL Encoder instead of treating URL encoding as HTML escaping.
Signals you escaped the wrong layer
&lt;appears visibly in the UI where users expected<.- A quote inside a username or title breaks an attribute value.
- A sanitized rich-text field gets decoded again before rendering.
- A URL parameter is HTML-escaped but still changes meaning after navigation.
- Tests cover clean names but not angle brackets, quotes, ampersands, or long pasted content.
A small test set catches most mistakes
You do not need a giant security lab to catch common escaping mistakes. Add a few boring fixtures to component tests, email previews, and template snapshots.
<strong>not bold</strong>should display as text in a text-only field.Sagar "Admin" & Teamshould not break attributes, JSON, or email templates.javascript:alert(1)should not be accepted as a user-controlled navigation target.Fish & Chips < Launchshould round-trip without becoming double-escaped.
The best escaping strategy is specific, boring, and easy to review: default framework escaping for text, URL encoding for URL parts, sanitization for trusted rich text, and no raw HTML unless the path is deliberately guarded.
Related Posts

Color Palettes: Check Contrast Before the UI Ships
Build palettes that hold up in real interfaces by checking contrast, states, tokens, and light/dark backgrounds before CSS changes ship.

UUIDs: Pick Identifiers That Survive Real Systems
Use UUIDs deliberately: choose the right identifier shape, keep IDs opaque, avoid collisions, and test how values move through APIs and databases.

Password Generators: Create Strong Secrets Without Leaks
Generate passwords safely by choosing length, randomness, and storage habits before a secret ever leaves your browser.