![Michał Bentkowski](https://web.dev/images/authors/securitymb.jpg) Michał Bentkowski

<br />


Published: June 12, 2025

<br />

On May 20, 2025, [the HTML specification was
updated](https://github.com/whatwg/html/pull/6362) to escape `<` and `>` in
attributes, helping prevent [mutation XSS](https://cure53.de/fp170.pdf) (mXSS)
vulnerabilities. This change landed in Chrome 138, which was promoted to Beta on
May 28, 2025, and will become Stable on June 24, 2025.

This post details the impact of the HTML attribute escaping change on web
developers and potential breakages; the security rationale behind this change is
explained in our [related post](https://bughunters.google.com/blog/5038742869770240) on the Security Engineering blog.

## What changed

Suppose that you have a `<div>` element whose attribute `data-content` has a
value of `"<u>hello</u>"`. What happens when you read `div.outerHTML`?

Historically, you'd get the following HTML:

    <div data-content="<u>hello</u>"></div>

After the change, you'll get the following HTML:

    <div data-content="&lt;u&gt;hello&lt;/u&gt;"></div>

Previously, neither `<` nor `>` were escaped in attributes. Now, both of these
characters are always escaped.

## What didn't change

The change exclusively modifies how HTML fragments are converted back into a
string representation during serialization. The impact is limited to scenarios
where the `innerHTML` or `outerHTML` properties are accessed, or when the
`getHTML()` method is invoked on an element. These operations take the existing
DOM structure and produce a textual HTML representation.

This change does *not* affect HTML parsing. Consider the following HTML:

    <div id="div1" data-content="<u>hello</u>"></div>
    <div id="div2" data-content="&lt;u&gt;hello&lt;/u&gt;"></div>

Both `div`s will be parsed exactly the same way and in both cases
`div.dataset.content` will return `"<u>hello</u>"`.

## What won't break?

If you use any DOM API, such as
[`getAttribute`](https://developer.mozilla.org/docs/Web/API/Element/getAttribute),
[`getAttributeNS`](https://developer.mozilla.org/docs/Web/API/Element/getAttributeNS),
[`dataset`](https://developer.mozilla.org/docs/Web/API/HTMLElement/dataset),
or
[`attributes`](https://developer.mozilla.org/docs/Web/API/Element/attributes),
to retrieve attribute values, they will return the same decoded values as
before, specifically with `<` and `>` decoded.

Consider the following example, in which all `console.log` lines will log
`"<u>"`:

    <div data-content="&lt;u&gt;"></div>

    const div = document.querySelector("div");
    // All of the following will log "<u>"
    console.log(div.getAttribute("data-content"));
    console.log(div.dataset.content);
    console.log(div.attributes['data-content'].value);

## What can break?

### innerHTML and outerHTML to get attributes

If you use `innerHTML` or `outerHTML` to extract the value of an attribute, your
code can break. Consider the following, albeit slightly convoluted, example:

    <div data-content="<u>"></div>

    const div = document.querySelector("div");
    const content = div.outerHTML.match(/"([^"]+)"/)[1];
    console.log(content);

This code will exhibit different behavior after this change. Previously,
`content` would've been equal to `"<u>"` but now it is `"&lt;u&gt;"`.

Note that [parsing HTML with regular expressions is not
recommended](https://stackoverflow.com/a/1732454). If you need to get a value of
an attribute, use the DOM APIs described in previous sections.

### End-to-end tests

If you have a CI/CD pipeline where you employ Chromium to generate HTML, and
you've written tests to compare the HTML to a static expected value, these tests
can break if any attribute contains `<` or `>`.

This is an expected breakage---you need to update the expected value so that all
`<` and `>`characters are escaped to `&lt;` and `&gt;,` respectively.

## Summary

This blog post described a change in the HTML specification which will lead
browsers to start escaping `<` and `>` in attributes to improve security by
preventing some instances of mutation XSS.

The change will be available for all users on June 24, 2025 on Chromium (version 138)
and Firefox (version 140). It's also included in Safari 26 Beta which
should be released around September 2025.

If you believe that this change broke your website and you don't have an easy
way to fix it, please file a bug at
<https://issues.chromium.org/>.

## Additional information

- [Original bug report about this proposed change](https://github.com/whatwg/html/issues/6235).
- [Pull request that changes the spec](https://github.com/whatwg/html/pull/6362).
- [ChromeStatus entry](https://chromestatus.com/feature/6264983847174144)
- [Google Security Engineering blog post on security rationale behind this change](https://bughunters.google.com/blog/5038742869770240)