Cookies over HTTP

Handle cross-site and partitioned cookies

Distinguish SameSite rules from partitioned cookies and avoid depending on unrestricted third-party state.

When your content is embedded in someone else’s page, a cookie has two contexts. Who set it, and which top-level site the browser is showing. Two different mechanisms deal with that, and people mix them up.

SameSite decides delivery

SameSite answers one question: should the browser attach this cookie to a cross-site request, where the top-level site differs from the cookie’s site?

  • Strict: never. Even a link from another site to yours arrives without the cookie
  • Lax: only on top-level navigations with safe methods like GET. Not on embedded requests, not on cross-site POST
  • None: always. The browser requires Secure alongside it

For the field-notes session, Lax is right. Users click a link from an email and arrive logged in, but a hidden form on another site can’t post as them.

Now suppose our app ships an embeddable widget, an iframe that shows a shared note on other people’s sites. Its requests are cross-site by definition, so it needs SameSite=None.

Partitioned decides the storage key

SameSite=None used to mean one cookie jar for the widget, shared across every site that embeds it. That’s the classic third-party cookie, and browsers restrict it because it’s also how cross-site tracking works.

The Partitioned attribute changes the storage key. The cookie is stored under “widget origin, inside top-level site X”. Embedded on hikingclub.org you get one jar. Embedded on trailmaps.net you get another. Same browser, same widget server, two separate cookies.

Set-Cookie: __Host-widget=compact; Path=/; Secure; SameSite=None; Partitioned

This is a fair trade. The widget keeps per-site state, like a collapsed panel or a preferred language. It can’t recognize the same person across sites.

Partitioning doesn’t make the cookie first-party, and it doesn’t let the widget read the host site’s cookies. It only splits the widget’s own jar.

Design for zero cross-site cookies

Browser policies here keep changing. Safari and Firefox block unpartitioned third-party cookies by default. Chrome’s rules keep shifting.

My advice is to design embeds to work without cross-site cookies at all, and treat partitioned state as a bonus. Pass what the widget needs in the URL or through postMessage from the host page. Then test with third-party cookies blocked in every browser you support.

Draw the keys

Take one widget embedded on two unrelated sites and draw what the widget server receives from each site with:

  1. an unpartitioned SameSite=None cookie
  2. the Partitioned cookie above
  3. no cross-site cookie access at all

In the first case both sites send the same value. In the second, each site sends its own. In the third, nothing arrives and the widget must still work. If your design only survives case one, it’s already broken for many users.

Lesson completed