Securely embed content onto a page without sharing cross-site data.
Implementation status
This document outlines a new HTML element: <fencedframe>
.
- The Fenced Frames proposal is now in general availability.
- Chrome Platform Status
Proposal | Status |
---|---|
Web API changes for urn to config Explainer |
Available in Chrome in Q1 2023. |
Creative Macros in Fenced Frames for Ads Reporting (FFAR) GitHub Issue |
Available in Chrome in Q3 2023. |
Send Automatic Beacons Once GitHub Issue |
Available in Chrome in Q3 2023. |
Serializable Fenced Frames Configs GitHub Issue |
Available in Chrome in Q3 2023. |
Additional Format Option for Protected Audience Ad Size Macros GitHub Issue |
Available in Chrome in Q4 2023. |
Automatic beacons sending to all registered URLs GitHub Issue | GitHub Issue |
Available in Chrome in Q4 2023. |
Enable Leaving Ad Interest Groups from Urn iFrames and Ad Component Frames
GitHub issue |
Available in Chrome in Q1 2024 |
Introduce reserved.top_navigation_start/commit
GitHub issue, GitHub issue |
Available in Chrome in Q1 2024 |
Do Not Disable Cookie Setting in ReportEvent until 3PCD
GitHub issue |
Available in Chrome in Q1 2024 |
Add support for automatic beacons in cross-origin subframes
GitHub issue |
Available in Chrome in Q1 2024 |
Allow Cross-Origin Subframes to Send reportEvent() Beacons
GitHub issue |
Available in Chrome in Q2 2024 |
Why do we need fenced frames?
A fenced frame (<fencedframe>
) is an HTML element for embedded
content, similar to an iframe. Unlike iframes, a fenced frame restricts
communication with its embedding context to allow the frame access to cross-site
data without sharing it with the embedding context. Some Privacy Sandbox APIs
may require select documents to render within a fenced frame.
Similarly, any first-party data in the embedding context cannot be shared with the fenced frame.
For example, let's say news.example
(the embedding context) embeds an ad from
shoes.example
in a fenced frame. news.example
cannot exfiltrate data from
the shoes.example
ad, and shoes.example
cannot learn first-party data from
news.example
.
Strengthen cross-site privacy with storage partitioning
While browsing the web, you've probably looked at products on one site, and then you've seen them appear again in an ad on a completely different site.
Today, this advertising technique is achieved primarily through tracking technology that uses third-party cookies to share information across sites. This is technology which Chrome has committed to phase out and replace with more privacy-preserving variants.
Chrome is working on storage
partitioning, which
separates browser storage per-site. Currently, if an iframe from shoes.example
is embedded on news.example
, and that iframe stores a value into storage,
then that value can be read from the shoes.example
site. When storage has been
partitioned, cross-site iframes will no longer share storage, therefore
shoes.example
will not be able to access information stored by the iframe. If
the iframe is served from *.shoes.example
and embedded on
*.shoes.example
, browser storage will be shared as these are considered same-site.
Storage partitioning will be applied to standard storage APIs including LocalStorage, IndexedDB, and cookies. In a partitioned world, information leakage across first-party storage will be significantly reduced.
Work with cross-site data
Fenced frames is a Privacy Sandbox feature which suggests top-level sites should partition data. Many Privacy Sandbox proposals and APIs aim to satisfy cross-site use cases without third-party cookies or other tracking mechanisms. For example:
- Protected Audience API allows for interest-based ad serving in a privacy-preserving manner.
- Shared Storage allows access to unpartitioned cross-site data in a secure environment.
Let's consider how fenced frames could work with the Protected Audience API. With the Protected Audience API, a user's interests are registered on an advertiser's site in interest groups, along with ads that may be of interest to the user. Then, on a separate site (known as a "publisher"), the ads registered in relevant interest groups are auctioned and the winning ad is displayed in a fenced frame.
If the publisher displays the winning ad in an iframe and the script can read the
iframe's src
attribute, the publisher can infer information about the visitor's
interests from that ad's URL. This is not privacy-preserving.
With a fenced frame, the publisher could display an ad which matches visitor
interests, but the src
and interest group will be known only to the advertiser
in the frame. The publisher could not access this information.
How do fenced frames work?
Fenced frames use the FencedFrameConfig
object for navigation. This object can be returned from a Protected Audience API auction or Shared Storage's URL selection operation. Then, the config object is set as the config
attribute on the fenced frame element. This differs from an iframe where a URL or opaque URN is assigned to the src
attribute. The FencedFrameConfig
object has a read-only url
property; however, since the current use-cases require the actual URL of the internal resource to be hidden, this property returns the string opaque
when read.
A fenced frame can't use postMessage
to communicate with its embedder. However, a fenced frame can use postMessage
with iframes inside the fenced frame.
Fenced frames will be isolated from the publisher in other ways. For instance
the publisher won't have access to the DOM inside of a fenced frame, and the
fenced frame cannot access the publisher's DOM. Further, attributes such as
name
—which can be set to any value to and observed by the
publisher—aren't available in fenced frames.
Fenced frames behave like a top-level browsing
context
(such as a browser tab). Although a fenced frame in certain use cases
(such as opaque-ads
) can contain cross-site data (such as a Protected Audience API interest
group), the frame cannot access unpartitioned storage or cookies. An
opaque-ads
fenced frame can access a unique, nonce-based cookie and storage
partition.
The characteristics of fenced frames are further detailed in the explainer.
How do fenced frames compare to iframes?
Now that you know what fenced frames will and won't do, it's useful to compare to existing iframe features.
Feature | iframe |
fencedframe |
---|---|---|
Embed content | Yes | Yes |
Embedded content can access embedding context DOM | Yes | No |
Embedding context can access embedded content DOM | Yes | No |
Observable attributes, such as name |
Yes | No |
URLs (http://example.com ) |
Yes | Yes (dependent on use case) |
Browser-managed opaque source (urn:uuid ) |
No | Yes |
Access to cross-site data | No | Yes (dependent on use case) |
Fenced frames support fewer external communication options to preserve privacy.
Will fenced frames replace iframes?
Ultimately, fenced frames won't replace iframes and you won't have to use them. Fenced frames are a more private frame for usage when data from different top-level partitions needs to be displayed on the same page.
Same-site iframes (sometimes known as friendly iframes) are considered trusted content.
Use fenced frames
Fenced frames will work in combination with other Privacy Sandbox APIs to display documents from different storage partitions within a single page. Potential APIs are currently in discussion.
Current candidates for this combination include:
- From the TURTLEDOVE API family (which is the basis for the Protected Audience API), fenced frames could work with Conversion Lift Measurement using Shared Storage.
- Another option is to allow fenced frames to be read-only or access unpartitioned storage.
For more details, refer to the Fenced Frames use cases explainer.
Examples
To obtain a fenced frame config
object, you must pass in resolveToConfig: true
to Protected Audience API's runAdAuction()
call or Shared Storage's selectURL()
call. If the property is not added (or is set to false
), the resulting promise will resolve to a URN that can only be used in an iframe.
const frameConfig = await navigator.runAdAuction({ // ...auction configuration resolveToConfig: true });
const frameConfig = await sharedStorage.selectURL('operation-name', { resolveToConfig: true });
Once you have obtained the config, you can assign it to a fenced frame's config
attribute to navigate the frame to the resource represented by the config. Older versions of Chrome don't support the resolveToConfig
property, so you must still confirm that the promise resolved to a FencedFrameConfig
before navigating:
if (window.FencedFrameConfig && frameConfig instanceof FencedFrameConfig) { const frame = document.createElement('fencedframe'); frame.config = frameConfig; }
To learn more, see the Fenced Frame and Fenced Frame config explainers.
Headers
Browsers will set Sec-Fetch-Dest: fencedframe
for requests made from fenced frames and iframes that are embedded within a fenced frame.
Sec-Fetch-Dest: fencedframe
The server must set the Supports-Loading-Mode: fenced-frame
response header for a document to be loaded in a fenced frame. The header must be present for any iframes inside of a fenced frame, as well.
Supports-Loading-Mode: fenced-frame
Shared Storage context
You may want to use Private Aggregation to report event-level data in fenced frames associated with contextual data from the embedder. By using the fencedFrameConfig.setSharedStorageContext()
method, you can pass some contextual data, such as an event ID, from the embedder to shared storage worklets initiated by the Protected Audience API.
In the following example, we store some data available on the embedder page and some data available in the fenced frame in shared storage. From the embedder page, a mock event ID is set as the shared storage context. From the fenced frame, the frame event data is passed in.
From the embedder page, you can set contextual data as the shared storage context:
const frameConfig = await navigator.runAdAuction({ resolveToConfig: true });
// Data from the embedder that you want to pass to the shared storage worklet
frameConfig.setSharedStorageContext('some-event-id');
const frame = document.createElement('fencedframe');
frame.config = frameConfig;
From the fenced frame, you can pass in event-level data from the frame into the shared storage worklet (unrelated to the contextual data from the embedder above):
const frameData = {
// Data available only inside the fenced frame
}
await window.sharedStorage.worklet.addModule('reporting-worklet.js');
await window.sharedStorage.run('send-report', {
data: {
frameData
},
});
You can read the embedder's contextual information from sharedStorage.context
and the frame's event-level data from the data
object, then report them through Private Aggregation:
class ReportingOperation {
convertEventIdToBucket(eventId) { ... }
convertEventPayloadToValue(info) { ... }
async run(data) {
// Data from the embedder
const eventId = sharedStorage.context;
// Data from the fenced frame
const eventPayload = data.frameData;
privateAggregation.contributeToHistogram({
bucket: convertEventIdToBucket(eventId),
value: convertEventPayloadToValue(eventPayload)
});
}
}
register('send-report', ReportingOperation);
To learn more about the embedder's context in a fenced frame config object, see the explainer.
Try fenced frames
Use Chrome
flags to
enable the Fenced Frame API at chrome://flags/#enable-fenced-frames
.
There are multiple choices in the dialog. We strongly recommend you select *Enable*, which allows Chrome to automatically update to new architecture as it becomes available.
The other options, Enabled with ShadowDOM and Enabled with multiple page architecture, offer different implementation strategies which are only relevant to browser engineers. Today, Enable works in the same way as Enabled with ShadowDOM. In the future, Enable will map to Enable with multiple page architecture.
Feature detection
To determine if fenced frames are defined:
if (window.HTMLFencedFrameElement) {
// The fenced frame element is defined
}
To determine if the fenced frame config is available:
js
if (window.FencedFrameConfig && frameConfig instanceof FencedFrameConfig) {
// The fenced frame config is available
}
Browser support
The <fencedframe>
element is still in experimental mode, so it is currently
supported from Chrome 97 onwards. At this time, it's not supported by other
browsers.
Engage and share feedback
Fenced Frames are under active discussion and subject to change in the future. If you try this API and have feedback, we'd love to hear it.
- GitHub: Read the explainer, raise questions, and follow discussion.
- Developer support: Ask questions and join discussions on the Privacy Sandbox Developer Support repo.