By Milica Mihajlija, Jeremy Ney and Sarino Grasso
First-Party Sets (FPS) is a web platform mechanism which helps browsers to understand the relationships amongst a collection of domains. This allows browsers to make key decisions to enable certain site functions (such as whether to allow access to cross-site cookies) and to present this information to users.
As Chrome deprecates third-party cookies, its goal is to maintain key use cases on the web while improving privacy for users. For example, many sites rely on multiple domains to serve a single user experience. Organizations may want to maintain different top-level domains for multiple use cases like country specific domains or service domains for hosting images or video. FPS allows sites to share data across domains, with specific controls.
What is a First-Party Set
At a high level, a First-Party Set is a collection of domains, for which there is a single "set primary" and potentially multiple "set members".
In the example below, primary
lists the primary domain, and associatedSites
lists domains that meet the requirements of the associated subset.
{
"primary": "https://primary.com",
"associatedSites": ["https://associate1.com", "https://associate2.com", "https://associate3.com"]
}
The canonical FPS list is a publicly viewable list in a JSON file format hosted in the FPS GitHub repository, which serves as the source-of-truth for all sets. Chrome consumes this file to apply to its behavior.
Only those with administrative control over a domain can create a set with that domain. Submitters are required to declare the relationship between each "set member" to its "set primary". Set members could include a range of different domain types and must be part of a subset based on a use case.
If your application depends on access to cross-site cookies (also called third-party cookies) across sites within the same First-Party Set, you can use Storage Access API (SAA) and the requestStorageAccessFor API to request access to those cookies. Depending on the subset that each site is part of, the browser may handle the request differently.
To learn more about the process and requirements for submitting sets, check out the submission guidelines. Submitted sets will go through various technical checks to validate the submissions.
FPS use cases
First-Party Sets are a good match for cases when an organization needs a form of shared identity across different top-level sites.
Some of the use cases for FPS are:
- Country customization. Leveraging localized sites while relying on shared infrastructure (example.co.uk may rely on a service hosted by example.ca).
- Service domain integration. Leveraging service domains that users never directly interact with, but provide services across the same organization's sites (example-cdn.com).
- User content separation. Accessing data on different domains that separate user-uploaded content from other site content for security reasons, while allowing the sandboxed domain access to authentication (and other) cookies. If you are serving inactive user-uploaded content, you may also be able to safely host it on the same domain by following best practices here.
- Embedded authenticated content. Supporting embedded content from across affiliated properties (videos, documents, or resources restricted to the user signed in on the top-level site).
- Sign-in. Supporting sign-in across affiliated properties. The FedCM API may also be appropriate for some use cases.
- Analytics. Deploying analytics and measurement of user journeys across affiliated properties to improve quality of services.
FPS integration details
Storage Access API
The Storage Access API (SAA) provides a way for embedded cross-origin content to access the storage that it would normally only have access to in a first-party context.
Embedded resources can use SAA methods to check whether they currently have access to storage, and to request access from the user agent.
When third-party cookies are blocked, but First-Party Sets are allowed, Chrome will automatically grant that permission for sites within the set and deny it for sites outside the set.
SAA is shipping in several browsers, however there are differences between browser implementations in the rules of handling storage access.
Checking and requesting storage access
To check whether they currently have access to storage embedded sites can use Document.hasStorageAccess()
method.
The method returns a promise that resolves with a boolean value indicating whether the document already has access to its cookies or not. The promise also returns true if the iframe is same-origin as the top frame.
- Chrome 113, Supported 113
- Firefox 65, Supported 65
- Edge 85, Supported 85
- Safari 11.1, Supported 11.1
To request access to cookies in a cross-site context embedded sites can use Document.requestStorageAccess()
(rSA).
When called, the method requires a user gesture to resolve, otherwise it will throw an exception. It returns a promise that resolves if the access to storage was granted, and rejects if access was denied.
- Chrome 113, Supported 113
- Firefox 65, Supported 65
- Edge 113, Supported 113
- Safari 11.1, Supported 11.1
requestStorageAccessFor in Chrome
SAA only allows embedded sites to request access to storage from within <iframe>
elements that have received user interaction.
This poses challenges in adopting SAA for top-level sites that use cross-site images or script tags requiring cookies.
To address this, Chrome has implemented a way for top-level sites to request storage access on behalf of specific origins with Document.requestStorageAccessFor()
(rSAFor).
document.requestStorageAccessFor('https://target.site')
Checking storage access permissions
Access to some browser features, like camera or geolocation, is based on user-granted permissions. The Permissions API provides a way to check the permission status for accessing an API–whether it has been granted, denied, or it requires some form of user interaction, such as clicking a prompt or interacting with the page.
You can query permission status using navigator.permissions.query()
.
To check the storage access permission for the current context you need to pass in the 'storage-access'
string:
navigator.permissions.query({name: 'storage-access'})
To check the storage access permission for a specified origin, you need to pass in the 'top-level-storage-access'
string:
navigator.permissions.query({name: 'top-level-storage-access', requestedOrigin: 'https://target.site'})
Note that to protect the integrity of the embedded origin, this checks only permissions granted by the top-level document using document.requestStorageAccessFor
.
Depending on whether the permission can be automatically granted or it requires a user gesture, it will return prompt
or granted
.
Per frame model
rSA grants apply per frame. rSA and rSAFor grants are treated as separate permissions.
Each new frame will need to request storage access individually and it will automatically be granted access. Only the first request requires user gesture, any subsequent requests initiated by the iframe, such as navigation or subresources will not need to wait for a user gesture as that will be granted for the browsing session by the initial request.
Refreshing, reloading, or otherwise recreating the iframe will require requesting access again.
Security
For rSAFor, subresource requests require Cross-Origin Resource Sharing (CORS) headers or crossorigin
attribute on the resources, ensuring explicit opt-in.
Implementation examples
Requesting access to storage from an embedded cross-origin iframe

Check if you have storage access
To check if you already have storage access use document.hasStorageAccess()
.
If the promise resolves true, you can access storage in the cross-site context. If it resolves false, you need to request storage access.
document.hasStorageAccess().then((hasAccess) => {
if (hasAccess) {
// You can access storage in this context
} else {
// You have to request storage access
}
});
Request storage access
If you need to request storage access, first check the storage access permission navigator.permissions.query({name: 'storage-access'})
to see if that requires a user gesture or it can be automatically granted.
If the permission is granted
you can call document.requestStorageAccess()
and it should succeed without a user gesture.
If the permission status is prompt
you need to initiate the document.requestStorageAccess()
call after a user gesture, such as a button click.
If you try calling requestStorageAccess()
before the permission is granted or a user gesture is registered, it will throw an error: "requestStorageAccess: Must be handling a user gesture to use".
Example:
navigator.permissions.query({name: 'storage-access'}).then(res => {
if (res.state === 'granted') {
// Permission has already been granted
// You can request storage access without any user gesture
rSA();
} else if (res.state === 'prompt') {
// Requesting storage access requires user gesture
// For example, clicking a button
const btn = document.createElement("button");
btn.textContent = "Grant access";
btn.addEventListener('click', () => {
// Request storage access
rSA();
});
document.body.appendChild(btn);
}
});
function rSA() {
if ('requestStorageAccess' in document) {
document.requestStorageAccess().then(
(res) => {
// Use storage access
},
(err) => {
// Handle errors
}
);
}
}
Subsequent requests from within the frame, navigations or subresources, will automatically have permission for accessing cross-site cookies. hasStorageAccess()
returns true and cross-site cookies from the same First-Party Set will be sent on those requests without any additional JavaScript calls.
Top-level sites requesting cookie access on behalf of cross-origin sites

Top-level sites can use requestStorageAccessFor()
to request storage access on behalf of specific origins.
hasStorageAccess()
only checks whether the site calling it has storage access, so a top level site can check the permissions for another origin.
To discover if the user will be prompted or if storage access has already been granted to specified origin, call navigator.permissions.query({name: 'top-level-storage-access', requestedOrigin: 'https://target.site'})
.
If the permission is granted
you can call document.requestStorageAccessFor('https://target.site')
. It should succeed without a user gesture.
If the permission is prompt
then you will need to hook the document.requestStorageAccessFor('https://target.site')
call behind the user gesture, such as a button click.
Example:
navigator.permissions.query({name:'top-level-storage-access',requestedOrigin: 'https://target.site'}).then(res => {
if (res.state === 'granted') {
// Permission has already been granted
// You can request storage access without any user gesture
rSAFor();
} else if (res.state === 'prompt') {
// Requesting storage access requires user gesture
// For example, clicking a button
const btn = document.createElement("button");
btn.textContent = "Grant access";
btn.addEventListener('click', () => {
// Request storage access
rSAFor();
});
document.body.appendChild(btn);
}
});
function rSAFor() {
if ('requestStorageAccessFor' in document) {
document.requestStorageAccessFor().then(
(res) => {
// Use storage access
},
(err) => {
// Handle errors
}
);
}
}
After a successful requestStorageAccessFor()
call, cross-site requests will include cookies if they include CORS or the crossorigin attribute, so sites may want to wait before triggering a request.
The requests must use the credentials: 'include'
option and resources must include crossorigin="use-credentials"
attribute.
function checkCookie() {
fetch('https://first-party-sets.glitch.me/getcookies.json', {
method: 'GET',
credentials: 'include'
})
.then((response) => response.json())
.then((json) => {
// Do something
});
}
How to test locally
Prerequisites
To test FPS locally, use Chrome 113 or higher launched from the command line.
To preview upcoming Chrome features before they're released, download the Beta or Canary version of Chrome.
Example
To see a FPS demo in action, visit https://first-party-sets.glitch.me/.
To enable FPS locally, you need to use Chrome's --enable-features
option with a comma-separated list of flags that are explained in this section.
Learn more about how to run Chromium with flags.
--enable-features="FirstPartySets,StorageAccessAPI,StorageAccessAPIForOriginExtension,PageInfoCookiesSubpage,PrivacySandboxFirstPartySetsUI" \
--use-first-party-set="{\"primary\": \"https://first-party-sets.glitch.me\", \"associatedSites\": [\"https://fps-member-1.glitch.me\"]}" \
https://first-party-sets.glitch.me/
FirstPartySets
enables FPS in Chrome.StorageAccessAPI
enables Storage Access API.StorageAccessAPIForOriginExtension
enables top-level sites to use requestStorageAccessFor() to request storage access on behalf of specific origins.PageInfoCookiesSubpage
enables showing FPS in the PageInfo section accessible from the URL bar.PrivacySandboxFirstPartySetsUI
enables FPS UI "Allow related sites to see your activity in the group" option in Chrome settings, under Privacy and Security → Cookies and other site data (chrome://settings/cookies).
Declare a set locally
To declare a set locally, create a JSON object that contains URLs that are members of a set and pass it to --use-first-party-set
.
--use-first-party-set="{\"primary\": \"https://first-party-sets.glitch.me\", \"associatedSites\": [\"https://fps-member-1.glitch.me\"]}" \
https://first-party-sets.glitch.me/
Verify that third-party cookies are blocked
- In Chrome settings, go to Privacy and Security → Cookies and other site data or chrome://settings/cookies.
- Under General settings ensure that "Block third-party cookies" is enabled.
- Check that the sub-option "Allow related sites to see your activity in the group" is also enabled.

Verify that you have access to cross-site cookies
Call the APIs (rSA or rSAFor) from the sites that are being tested and validate access to the cross-site cookies.
FPS submission process
To declare the relationship amongst the domains and specify which subset they are part of, follow the steps below:
- Identify the relevant domains, this includes the set primary and set members, that will be part of the FPS. Also identify which subset type each set member belongs to.
- Ensure the set formation requirements and set validation requirements are in place.
- Declare the FPS in the correct JSON format.
- Submit the First Party Set by creating a pull request (PR) to the
first_party_sets.JSON
where Chrome will host the canonical FPS list. (A GitHub account is required to create PRs, and you will need to sign a Contributor's License Agreement (CLA) to contribute to the list.)
Once the PR is created, a series of checks will happen to validate that the requirements from step 2 are in place.
Before creating a PR, you can test your submission locally to see if it passes the checks.
If successful, the PR will indicate that checks have been passed. Approved PRs will be manually merged in batches to the canonical FPS list once per week (Tuesdays at 12pm Eastern Time).
If any of the checks fails, the submitter will be notified through a PR failure on GitHub. The submitter can fix the errors and update the PR, and keep in mind that:
- When the PR fails, an error message will provide additional information on why the submission may have failed (example).
- All technical checks governing set submissions are conducted on GitHub, and consequently all submission failures resulting from technical checks will be viewable on GitHub.
Enterprise policies
To meet the needs of enterprise users Chrome has a couple of enterprise policies in place:
- Systems that might not be able to integrate with First-Party Sets can disable the First-Party Sets feature in all enterprise instances of Chrome with the
FirstPartySetsEnabled
policy. - Some enterprise systems have internal only sites (such as an intranet) with registrable domains that differ from the domains in their First-Party Set. If they need to treat these sites as part of their First-Party Sets without exposing them publicly (as the domains may be confidential) they can augment or override their public First-Party Sets list with the
FirstPartySetsOverrides
policy.
Share feedback
Submitting a set on GitHub and working with the Storage Access API and the requestStorageAccessFor
API are opportunities to share your experience with the process and any issues you run into.
To join discussions about the First Party Sets:
- Join the First-Party Sets public mailing list.
- Raise issues and follow the discussion on First-Party Sets GitHub repo.
Updated on • Improve article