Cookies inside iframes
A cookie in a cross-site iframe is a third-party cookie, and it needs SameSite=None and Secure to be sent at all. Safari blocks it by default, and Firefox keeps a separate copy for each top site. Partitioned cookies and the Storage Access API work around these limits.
6 min read · Updated
First-party or third-party
A browser compares the site of the framed page with the site of the top page. A site is the scheme plus the registrable domain, for example https://partner.example.
| Top page | Framed page | Cookies in the frame |
|---|---|---|
https://www.your-site.com | https://app.your-site.com | Same site. They act like first-party cookies. |
https://partner.example | https://your-site.com | Cross-site. They are third-party cookies. |
All the problems below apply only to the cross-site case. If you control both sites, the simplest fix is to put the frame on a subdomain of the top site.
Step 1: SameSite=None and Secure
The SameSite attribute decides if a cookie goes with cross-site requests:
| SameSite value | Sent in a cross-site iframe? |
|---|---|
Strict | No |
Lax | No |
None | Yes, if the browser allows third-party cookies |
| Not set | Chrome treats it as Lax, so no |
A cookie with SameSite=None must also have Secure. Without Secure, the browser rejects the cookie. So the page must also use HTTPS.
Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=None
This is required, but it is often not enough. The browser can still block the cookie.
Step 2: know what each browser blocks
| Browser | Third-party cookies by default |
|---|---|
| Safari | Blocked by Intelligent Tracking Prevention |
| Firefox | Kept in a separate cookie store for each top site (Total Cookie Protection) |
| Chrome | Allowed. Blocked in Incognito and when the user turns on blocking |
In Firefox, your frame can set and read cookies. But the cookies from partner.example are not the same cookies your site has when the user visits it directly. A user who is signed in on your-site.com is not signed in inside the frame.
Test in Safari and in a Chrome Incognito window. Many users have these settings, and a page that works in normal Chrome can fail there.
Step 3: pick a fix that works in every browser
Option A: Partitioned cookies (CHIPS)
CHIPS means Cookies Having Independent Partitioned State. Add the Partitioned attribute, and the browser keeps one copy of the cookie for each top site:
Set-Cookie: __Host-session=abc123; Path=/; Secure; HttpOnly; SameSite=None; Partitioned
- A
Partitionedcookie must haveSecure. - The
__Host-prefix keeps the cookie on one host. It also needsPath=/and noDomain. - MDN marks
Partitionedas Baseline since December 2025. Chrome, Edge, Firefox, and Safari support it.
Use this when the frame needs its own state on each partner site, for example a chat widget or a cart. It does not share a login with your main site. The user signs in once for each top site.
Option B: the Storage Access API
The Storage Access API lets the frame ask for its unpartitioned cookies, the same cookies your site has as a first party.
// Runs inside the iframe, from a click handler
async function askForCookies() {
if (await document.hasStorageAccess()) return true;
try {
await document.requestStorageAccess();
location.reload(); // load again so the request sends the cookies
return true;
} catch {
return false; // show a "Sign in" button that opens a new tab
}
}
document.getElementById("enable").addEventListener("click", askForCookies);
Rules:
- Call
requestStorageAccess()from a click or tap. A call without a user gesture is denied, unless the user already granted access. - The user must have used your site directly, as a top page, recently. Firefox uses a 30-day window.
- Safari and Chrome show a prompt to the user. Firefox prompts only after an origin asks on several sites.
- If the iframe has a
sandbox, the parent must addallow-storage-access-by-user-activation,allow-scripts, andallow-same-origin. - Access fades after 30 days of browser use without user interaction. Each interaction extends it by 30 days. Firefox uses 30 calendar days.
Option C: no cookies in the frame
You can also skip cookies. The parent page gets a short-lived token from your API and sends it to the frame with postMessage. The frame keeps the token in memory and sends it in an Authorization header. This works in all browsers, but you must protect the token like a password.
Check the frame loads at all
Cookie problems come after the frame loads. Paste the framed URL into the iframe tester to rule out a blocked frame first. For sign-in loops, see Login doesn't stick in an iframe.
The cookie messages in DevTools
Each browser writes its own note when it blocks a cookie in a frame. These pages show the exact text, the cause, and the fix:
- Set-Cookie blocked: SameSite=Lax cookie in a cross-site frame (Chrome and Edge)
- Cookie has been rejected because it is in a cross-site context (Firefox)
- Third-party cookie blocked in Chrome
- Login loop in an iframe: the sign-in page loads again