HTML Academy · Lesson

XSS via innerHTML and How to Prevent It

Understand cross-site scripting and safe alternatives to innerHTML.

Lesson 2 of 414 steps

XSS via innerHTML and How to Prevent It is a free HTML Academy lesson on CoddyKit. This is lesson 2 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the HTML Academy learning path, and your progress syncs across the web and the CoddyKit app. The HTML Academy course includes 4 lessons in total.

The Core Vulnerability

Setting el.innerHTML = userInput parses the string as HTML. If userInput contains <script> tags or event-handler attributes (onclick, onerror), the browser interprets them — executing attacker-controlled code on the page.

Why It Is Common

Any code that interpolates user data into HTML — server templates, client-side rendering — risks XSS if the data is not escaped. Single-page apps that build innerHTML from API responses are especially exposed when those responses include user content.

A Concrete Example

A username "Bob<img src=x onerror=alert(1)>" set via div.innerHTML = `Welcome, ${user}` executes alert(1) when the img fails to load. The same payload as plain text (via textContent) is harmless — the browser sees only characters, not tags.

// VULNERABLE
div.innerHTML = `Hi, ${user.name}`;

// SAFE
div.textContent = `Hi, ${user.name}`;

Use textContent for Plain Text

textContent sets text only — HTML special characters appear as literal characters, never as tags. This is the right tool 90% of the time. Switch to innerHTML only when you actually need to render markup, not text.

Use DOM Methods for Structure

To create elements with user data, build them with createElement and textContent: const li = document.createElement("li"); li.textContent = name; ul.appendChild(li);. The result is structurally identical to innerHTML but XSS-safe by construction.

When You Must Use innerHTML

For server-generated trusted HTML (your own template output, sanitized rich-text from a trusted editor), innerHTML is fine. Never apply it directly to data from untrusted sources without sanitization.

Sanitize Rich Text with DOMPurify

If users submit rich text (a comment editor, markdown rendering), sanitize before setting innerHTML: el.innerHTML = DOMPurify.sanitize(richHtml). DOMPurify strips dangerous tags and attributes while keeping safe formatting like <b>, <em>, <a>.

insertAdjacentHTML Has Same Risk

el.insertAdjacentHTML("beforeend", html) parses HTML strings — same XSS surface as innerHTML. Same rules apply: never feed user data directly; sanitize or use DOM methods. Same goes for document.write, though no one should use that anymore.

Framework Defaults

React, Vue, Svelte, Angular all escape interpolated text by default — {name} is safe. They expose escape hatches (dangerouslySetInnerHTML in React, v-html in Vue) which carry the same XSS risk; use sparingly with sanitization.

Attribute Injection

Even attribute values can be vectors: <a href={url}> with url="javascript:alert(1)" executes when clicked. Validate URLs scheme-by-scheme (allow only http:, https:, mailto:, tel:) before placing user input in href, src, or other URL-bearing attributes.

Trusted Types Policy

Modern browsers support Trusted Types: configure CSP with require-trusted-types-for 'script' and innerHTML refuses raw strings — only policy-wrapped values pass. This makes XSS impossible by API rather than by discipline.

Defense in Depth

No single layer is enough. Combine: input validation on the server, output escaping on rendering, CSP to block injected scripts, Trusted Types to refuse raw strings, and security review for any innerHTML usage. Layered defense survives a bug in any one layer.

Knowledge Check

Why is el.textContent = userInput safe from XSS while el.innerHTML = userInput is dangerous?

Summary

innerHTML on user input is the classic XSS vector. Use textContent for text, createElement+textContent for structure, DOMPurify when rich HTML is required. Validate URL schemes for attribute values. Layer CSP and Trusted Types to neutralize bugs that slip through code review. Modern frameworks escape by default — keep their unsafe escape hatches rare and reviewed.

Free to start

Learn HTML with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
40
Lessons
159

Frequently Asked Questions

Is the “XSS via innerHTML and How to Prevent It” lesson free?

Yes — any 3 lessons of the HTML Academy learning path, including “XSS via innerHTML and How to Prevent It”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The HTML Academy course includes 4 lessons in total.

What will I learn in “XSS via innerHTML and How to Prevent It”?

Understand cross-site scripting and safe alternatives to innerHTML. You practise HTML Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start HTML Academy?

No prior experience is required. HTML Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.

How long does the “XSS via innerHTML and How to Prevent It” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this HTML Academy lesson?

Yes. Every HTML Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Content Security Policy meta http-equiv
  2. XSS via innerHTML and How to Prevent It
  3. iframe Sandboxing and Permissions Policy
  4. HTTPS and Subresource Integrity
← Back to HTML Academy