setHTMLUnsafe vs setHTML: Which One Sanitizes User HTML?

Formatted user comments now go through setHTMLUnsafe instead of innerHTML. Is that markup filtered, or does it still run as written?

setHTMLUnsafe vs setHTML: Which One Sanitizes User HTML?
TLDR
  • Replacing innerHTML with setHTMLUnsafe does not add default sanitization.
  • setHTML removes XSS-unsafe items even when a custom configuration allows them.
  • Firefox 158 changes javascript: URL handling in calls that apply a sanitizer.

The omitted option is the important difference

A newer HTML insertion API is not automatically a safer replacement for innerHTML. setHTMLUnsafe does not sanitize by default. Without a separate processing policy, such as a Trusted Types policy, a call that omits its sanitizer does not acquire a filtering step.

Passing sanitizer: "default" is an explicit choice in setHTMLUnsafe. For user-provided markup, prefer setHTML where it is supported.

CallSanitization performed by the method
setHTML(input)Uses the default sanitizer and removes XSS-unsafe content.
setHTMLUnsafe(input)Does not apply a sanitizer.
setHTMLUnsafe(input, { sanitizer: "default" })Explicitly applies the default sanitizer.

A variable named “safeHTML” does not change these rules. Nor does receiving user-authored content from your own server make that content trusted.

Choose a deliberate fallback for unsupported browsers

This small rendering function allows HTML formatting where setHTML is available and displays literal text elsewhere. Its target is an ordinary content div.

function showComment(target, input) {
  if (typeof target.setHTML === "function") {
    target.setHTML(input);
  } else {
    target.textContent = input;
  }
}

textContent does not parse HTML. A string containing <b>hello</b> therefore appears with its tags rather than as bold text. This is a reduced-format fallback, not a polyfill that preserves the same presentation.

If formatting is unnecessary, use textContent directly on the content element. If it must survive across all target browsers, keep an established sanitization path for those without setHTML. Falling back to raw innerHTML would undo the protection you intended to add.

Firefox 158 changes sanitized javascript: links

Firefox 158 adds the Sanitizer configuration’s javascriptURLs control and its associated setter. Unsafe parsing methods now strip javascript: URLs when they use the default sanitizer, or one on which removeUnsafe() has been called.

The condition matters: those calls have a sanitizer. The change does not turn a bare setHTMLUnsafe call into a safe one. Safe methods such as setHTML continue to remove these URLs.

If an update changes a link’s behavior, inspect the method and the configuration actually passed to it. Restoring executable URLs by disabling sanitization is a different decision from preserving an ordinary navigation link.

Follow the content through its final insertion

The default configuration removes more than script-related markup: comments and data-* attributes are among the additional removals. If an editor loses metadata, identify the specific attributes it needs instead of disabling the entire filter.

A custom allow list also behaves differently between the methods. setHTML still removes XSS-unsafe elements and attributes; an unsafe method does not impose that same safety baseline over your choices.

Finally, keep sanitization connected to insertion. The HTML standard’s security guidance explains that serializing sanitized nodes and parsing them again can introduce mutation XSS. Reading the result through innerHTML and assigning that string to another element’s innerHTML creates a new parsing step.

Review the fallback branch, custom configuration and final insertion together. The security result depends on that complete path, rather than the appearance of a new method name in the code.