Chrome, Firefox, Edge, and others are changing their default behavior in line with the IETF proposal, Incrementally Better Cookies so that:
- Cookies without a
SameSite
attribute are treated asSameSite=Lax
, meaning the default behavior is to restrict cookies to first party contexts only. - Cookies for cross-site usage must specify
SameSite=None; Secure
to enable inclusion in third party context.
If you haven't already done so, you should update the attributes for your third-party cookies so they won't be blocked in the future.
Use cases for cross-site or third-party cookies
There are a number of common use cases and patterns where cookies need to be sent in a third-party context. If you provide or depend on one of these use cases, make sure that either you or the provider are updating their cookies to keep the service functioning correctly.
Content within an <iframe>
Content from a different site displayed in an <iframe>
is in a third-party
context. Standard use cases include:
- Embedded content shared from other sites, such as videos, maps, code samples, and social posts.
- Widgets from external services such as payments, calendars, booking, and reservation features.
- Widgets such as social buttons or anti-fraud services that create less obvious
<iframes>
.
Cookies may be used here to, among other things, maintain session state, store general preferences, enable statistics, or personalize content for users with existing accounts.
Because the web is inherently composable, <iframes>
are also used to embed
content viewed in a top-level or first-party context. Any cookies the site
displayed in the iframe uses are considered third-party cookies. If you're
creating sites that you want other sites to embed, and need cookies to make them
work, you also need to ensure those are marked for cross-site usage or that you
can fall back gracefully without them.
"Unsafe" requests across sites
"Unsafe" might sound concerning here, but it refers to any request that might be
intended to change state. On the web, that's primarily POST requests. Cookies
marked as SameSite=Lax
are sent on safe top-level navigations, like clicking a
link to go to a different site. However, something like a <form>
submission to
a different site using POST doesn't include cookies.
This pattern is used for sites that can redirect the user out to a remote
service to perform some operation before returning, for example, redirecting to
a third-party identity provider. Before the user leaves the site, a cookie is
set containing a single use token with the expectation that this token can be
checked on the returning request to mitigate
Cross Site Request Forgery (CSRF)
attacks. If that returning request comes through POST, you'll need to mark the
cookies as SameSite=None; Secure
.
Remote resources
Any remote resource on a page, such as from <img>
tags or <script>
tags,
might rely on cookies being sent with a request. Common use cases include
tracking pixels and personalizing content.
This also applies to requests sent from your JavaScript using fetch
or
XMLHttpRequest
. If fetch()
is called with the
credentials: 'include'
option,
those requests are likely to include cookies.
For XMLHttpRequest
, expected cookies are usually indicated by a
withCredentials
value
fo true
. Those cookies must be appropriately marked to be included in
cross-site requests.
Content within a WebView
A WebView in a platform-specific app is powered by a browser. Developers need to test whether the restrictions or issues that affect their apps also apply to their app's WebViews.
Android also lets its platform-specific apps set cookies directly using the
CookieManager API.
As with cookies set using headers or JavaScript, consider including
SameSite=None; Secure
if they're intended for cross-site use.
How to implement SameSite
today
Mark any cookies that are only needed in a first-party context as SameSite=Lax
or SameSite=Strict
depending on your needs. If you don't mark these cookies
and instead rely on default browser behavior to handle them, they can behave
inconsistently across browsers and potentially trigger console warnings for each
cookie.
Set-Cookie: first_party_var=value; SameSite=Lax
Make sure to mark any cookies needed in a third-party context as
SameSite=None; Secure
. Both attributes are required. If you just specify
None
without Secure
, the cookie will be rejected. To account for differences
in browser implementations, you might need to use some of the mitigating
strategies described in Handle incompatible clients.
Set-Cookie: third_party_var=value; SameSite=None; Secure
Handle incompatible clients
Because these changes to include None
and update default behavior are still
relatively new, different browsers handle them in different ways. You can refer
to the updates page on chromium.org
for a list of known issues, but this list might not be exhaustive.
One possible workaround is to set each cookie in both the new and the old style:
Set-cookie: 3pcookie=value; SameSite=None; Secure
Set-cookie: 3pcookie-legacy=value; Secure
Browsers implementing the newer behavior set the cookie with the SameSite
value. Browsers that don't implement the new behavior ignore that value and set
the 3pcookie-legacy
cookie. When processing included cookies, your site should
first check for the presence of the new style of cookie and, then fall back to
the legacy cookie if it can't find a new one.
The following example shows how to do this in Node.js, using the Express framework and its cookie-parser middleware:
const express = require('express');
const cp = require('cookie-parser');
const app = express();
app.use(cp());
app.get('/set', (req, res) => {
// Set the new style cookie
res.cookie('3pcookie', 'value', { sameSite: 'none', secure: true });
// And set the same value in the legacy cookie
res.cookie('3pcookie-legacy', 'value', { secure: true });
res.end();
});
app.get('/', (req, res) => {
let cookieVal = null;
if (req.cookies['3pcookie']) {
// check the new style cookie first
cookieVal = req.cookies['3pcookie'];
} else if (req.cookies['3pcookie-legacy']) {
// otherwise fall back to the legacy cookie
cookieVal = req.cookies['3pcookie-legacy'];
}
res.end();
});
app.listen(process.env.PORT);
This approach requires you to do extra work setting redundant cookies and making changes at the point of both setting and reading the cookie. However, it should cover all browsers regardless of their behavior, and keep third-party cookies functioning.
As an alternative, you can detect the client using the user agent string when a
Set-Cookie
header is sent. Refer to the
list of incompatible clients,
and use an appropriate user agent detection library for your platform, for
example, the ua-parser-js library
on Node.js. This approach only requires you to make one change, but user agent
sniffing might not catch all affected users.
Support for SameSite=None
in languages, libraries, and frameworks
The majority of languages and libraries support the SameSite
attribute for
cookies. However, because the addition of SameSite=None
is still relatively
recent, you might need to work around some standard behavior for now.
These behaviors are documented in the
SameSite
examples repository on GitHub.
Getting help
Cookies are used everywhere on the web, and it's rare for any development team to have complete knowledge of where their site sets and uses them, especially in cross-site use cases. When you encounter an issue, it might be the first time anyone has encountered it, so don't hesitate to reach out:
- Raise an issue in the
SameSite
examples repository on GitHub. - Ask a question in the "samesite" tag on StackOverflow.
- For issues with Chromium's behavior, raise a bug in the Chromium issue tracker.
- Follow Chrome's progress on the
SameSite
updates page.