Chrome disables modifying document.domain

If your website relies on setting document.domain, your action is required.

What's changing, and why?

Starting in Chrome 115, websites will be unable to set document.domain: Chrome will make document.domain immutable. To communicate cross-origin, you need to use alternative approaches, such as postMessage() or the Channel Messaging API.

Note that this change will be rolled out progressively.

We expect other browsers to eventually deprecate and remove this functionality. Review the browser compatibility section for details.

Why make document.domain immutable?

document.domain was designed to get or set the origin's hostname. Many websites set document.domain to allow communication between same-site but cross-origin pages.

While this is a convenient technique, it introduces a security risk, because it relaxes the same-origin policy. Security concerns around document.domain have led to a change in the specification that warns users to avoid using it.

In detail: Why make document.domain immutable?

How document.domain is used today

Many websites set document.domain to allow communication between same-site but cross-origin pages.

Same-site but cross-origin sites have the same eTLD+1 but different subdomains.

Here's how document.domain was used up until now:

Let's say a page on https://parent.example.com embeds an iframe page from https://video.example.com. These pages have the same eTLD+1 (example.com) with different subdomains. When both pages' document.domain is set to 'example.com', the browser treats the two origins as if they are same-origin.

Set the document.domain for https://parent.example.com:

// Confirm the current origin of "parent.example.com"
console.log(document.domain);

// Set the document.domain
document.domain = 'example.com';
console.log(document.domain);

Set the document.domain for https://video.example.com:

// Confirm the current origin of "video.example.com"
console.log(document.domain);

// Set the document.domain
document.domain = 'example.com';
console.log(document.domain);

You could now create a cross-origin DOM manipulation on https://parent.example.com against https://video.example.com.

Websites set document.domain to make it possible for same-site documents to communicate more easily. Because this change relaxes the same-origin policy, the parent page is able to access the iframe's document and traverse the DOM tree, and vice versa.

This is a convenient technique, however it introduces a security risk.

Security concerns with document.domain

Security concerns around document.domain have led to a change in the specification that warns users to avoid using it.

For example, when two pages set document.domain, they can pretend as if they are the same-origin. This is particularly critical when these pages use a shared hosting service with different subdomains. Setting document.domain opens up access to all other sites hosted by that same service, which makes it easier for attackers to access your sites. This is possible because document.domain ignores the port number part of the domain.

To learn more about the security implications of setting document.domain, read the "Document.domain" page on MDN.

Browser compatibility

How do I know if my site is affected?

If your website is affected by this change, Chrome warns you in the DevTools Issues panel — this warning has been added in 2022. Notice the yellow flag in the upper right of DevTools.

Screenshot of the issue warning in DevTools

You can also run your site through the LightHouse deprecated API audit to find all APIs that are scheduled to be removed from Chrome.

If you have set up the Reporting API, Chrome has sent you deprecation reports to notify you of this upcoming deprecation. Learn more about how to use the Reporting API with either existing report collection services or by building your own in-house solution.

How do I see this change in action?

The change will be rolled out progressively, starting in Chrome 115. To see in this change in action even if it hasn't already rolled out in your Chrome browser, you can turn it on as follows:

  1. Open chrome://flags/#origin-agent-cluster-default
  2. Select Enable.
  3. Restart Chrome.

What alternatives can I use?

The best option is to not modify document.domain at all, for example by hosting the page and all constituent frames on the same origin. This works in all versions of all browsers. But this may require substantial re-work of an application, so it's worth to also look at alternatives that continue to support cross-origin accesses.

Use postMessage() or Channel Messaging API instead of document.domain

In most use cases, cross-origin postMessage() or Channel Messaging API can replace document.domain.

In the following example:

  1. https://parent.example.com requests https://video.example.com within an iframe to manipulate DOM by sending a message via postMessage().
  2. https://video.example.com manipulates DOM as soon as it receives the message and notify the success back to the parent.
  3. https://parent.example.com acknowledges the success.

On https://parent.example.com:

// Send a message to https://video.example.com
iframe.postMessage('Request DOM manipulation', 'https://video.example.com');

// Receive messages
iframe.addEventListener('message', (event) => {
  // Reject all messages except ones from https://video.example.com
  if (event.origin !== 'https://video.example.com') return;

  // Filter success messages
  if (event.data === 'succeeded') {
    // DOM manipulation is succeeded
  }
});

On https://video.example.com:

// Receive messages
window.addEventListener('message', (event) => {
  // Reject all messages except ones from https://parent.example.com
  if (event.origin !== 'https://parent.example.com') return;

  // Do a DOM manipulation on https://video.example.com.

  // Send a success message to https://parent.example.com
  event.source.postMessage('succeeded', event.origin);
});

Try it and see how it works. If you have specific requirements that won't work with postMessage() or Channel Messaging API, let us know on Twitter via @ChromiumDev or ask on Stack Overflow with a document.domain tag.

As a last resort, send the Origin-Agent-Cluster: ?0 header

If you have strong reasons to continue setting document.domain, you can send Origin-Agent-Cluster: ?0 response header along with the target document.

Origin-Agent-Cluster: ?0

The Origin-Agent-Cluster header instructs the browser whether the document should be handled by the origin-keyed agent cluster or not. To learn more about Origin-Agent-Cluster, read Requesting performance isolation with the Origin-Agent-Cluster header.

When you send this header, your document can continue to set document.domain even after it becomes immutable by default.

All other documents that require that behavior will also need to send an Origin-Agent-Cluster (note that document.domain has no effect if only one document sets it).

Configure OriginAgentClusterDefaultEnabled for enterprise policy

Optionally, your admin can configure OriginAgentClusterDefaultEnabled policy to false to make document.domain settable by default on Chrome instances across your organization. To learn more, read Chrome Enterprise Policy List & Management | Documentation.

Resources

Acknowledgements

Photo by Finan Akbar on Unsplash