A dog in disguise.

Chrome will disable modifying `document.domain` to relax the same-origin policy

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

Published on

Warning

If your website relies on relaxing the same-origin policy via document.domain, your action is required. Continue to read more about why this is changing or skip to alternative code for cross-origin communication

document.domain was designed to get or set the origin's hostname.

On Chrome, websites will be unable to set document.domain. They will need to use alternative approaches such as postMessage() or Channel Messaging API to communicate cross-origin. We're targeting Chrome 101 to ship this change at the earliest, but this is dependent on the response to the Intent to Ship.

If your website relies on same-origin policy relaxation via document.domain to function correctly, the site will need to send an Origin-Agent-Cluster: ?0 header, as will all other documents that require that behavior (note that document.domain has no effect if only one document sets it).

Why make document.domain immutable?

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

Key Term

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

Here's how it's used:

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. The current discussion with other browser vendors is moving in the same direction.

For example, if a hosting service provides different subdomains per user, an attacker can set document.domain to pretend they are the same-origin as another user's page. Further, an attacker can host a website under a shared hosting service, which serves sites through the same IP address with different port numbers. In that case, the attacker can pretend to be on the same-site-but-same-origin as yours. 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 "Document.domain" page on MDN.

Chrome plans to make document.domain immutable from Chrome 101.

Alternative cross-origin communication

At this time, you have two options to replace document.domain for your website.

Use postMessage() or Channel Messaging API

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.

Send Origin-Agent-Cluster: ?0 header to continue using document.domain

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.

Browser compatibility

Resources

Acknowledgements

Photo by Braydon Anderson on Unsplash

Last updated: Improve article

We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.