Back to Blog

Firefox 148's setHTML: The XSS Protection We Should Have Had Years Ago

Firefox just shipped setHTML in version 148, replacing the notorious innerHTML with something that actually sanitizes by default. Here's why this matters and what it means for your security posture.

SecurityWeb DevelopmentJavaScriptXSS
February 24, 2026
3 min read

Firefox 148 just dropped something that should have existed a decade ago: the setHTML() method. If you've ever written element.innerHTML = userInput and immediately felt that little voice in your head screaming "sanitize that!", this is for you.

The problem we've been living with

For years, innerHTML has been the default way to inject HTML into the DOM. It's fast, it's simple, and it's catastrophically insecure if you're not careful. The issue? It executes scripts by default. Feed it user input without proper sanitization, and you've just handed an attacker the keys to your application.

The classic blunder:

javascript
// DON'T DO THIS
element.innerHTML = `<div>${userComment}</div>`;

If userComment contains <img src=x onerror=alert('XSS')>, congratulations you just got owned.

What setHTML actually does

Firefox 148 introduces setHTML() as part of the Sanitizer API. Unlike innerHTML, it sanitizes content by default using a built-in sanitizer before injecting it into the DOM.

javascript
// This is safe
element.setHTML(userComment);

The browser strips out anything dangerous - script tags, event handlers, javascript: URLs - before it ever touches the page. No library needed. No regex hacks. Just native, browser-level protection.

Why this took so long

The Sanitizer API has been in development for years. The core challenge? Balancing security with backward compatibility. Developers have been relying on innerHTML's "execute everything" behavior for legitimate use cases. Changing that default would break half the internet.

The solution: introduce a new method. innerHTML stays exactly as dangerous as it's always been. setHTML() gives you the safe alternative. You opt in to security.

What this means for your codebase

If you're working on a production web app, here's the practical takeaway:

  1. Start using setHTML() for user-generated content. Anywhere you're taking input from users and rendering it back, switch to the new API.

  2. innerHTML isn't going anywhere. You still need it for trusted content you control. Just be intentional about when you use it.

  3. Check browser support. Firefox 148 has it now. Chrome and Safari will follow (the spec is standardized). Use feature detection:

    javascript
    if (element.setHTML) {
    element.setHTML(content);
    } else {
    // Fallback to a sanitization library
    element.innerHTML = DOMPurify.sanitize(content);
    }
  4. This doesn't replace CSP. Content Security Policy is still your first line of defense. setHTML is defense in depth.

The bigger picture

This is part of a broader shift in web security: making the secure option the easy option. For too long, doing the right thing required extra dependencies (DOMPurify, sanitize-html) and extra cognitive load. Developers are human. When the insecure path is easier, people will take it.

By baking sanitization into the platform, Firefox (and eventually the rest) is reducing the friction to do the right thing. That's how you actually move the needle on security at scale.

What I'm watching

The next question is adoption. How long until this becomes a standard recommendation in security audits? How long until frameworks like React or Vue start using it internally?

My bet: within 18 months, using innerHTML for user content will be a critical finding in any halfway-decent security audit. And good riddance.

If you're a Principal Engineer or tech lead, start auditing your codebase now. Find every innerHTML call. Ask yourself: is this trusted content? If not, it's a candidate for migration.

The tools to write secure code are finally getting better. Time to use them.

Share

Get new posts in your inbox

Architecture, performance, security. No spam.

Keep reading