CINXE.COM
SameSite=None: Known Incompatible Clients
<!DOCTYPE html> <head> <meta charset="utf-8"> <title>SameSite=None: Known Incompatible Clients</title> <link rel="stylesheet" href="/_stylesheets/@docsearch/style.css"> <link rel="stylesheet" href="/_stylesheets/default.css"> </head> <!-- Configure Google Analytics v4 --> <!-- Google tag (gtag.js) --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-24XP4PG02H"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-24XP4PG02H'); </script> <header> <a href="/"> <img alt="the Chromium logo" src="/_assets/icon-chromium-96.png" width="48" height="48"> <h2>The Chromium Projects</h2> </a> <div id="search"></div> </header> <div id="main-wrapper"> <nav id="sidebar-left"> <section> <a href="/chromium-projects">Home</a> <a href="/Home">Chromium</a> <a href="/chromium-os">ChromiumOS</a> </section> <section> <h4>Quick links</h4> <a href="/for-testers/bug-reporting-guidelines">Report bugs</a> <a href="/developers/discussion-groups">Discuss</a> </section> <section> <h4>Other sites</h4> <a href="https://blog.chromium.org/">Chromium Blog</a> <a href="https://developer.chrome.com/extensions">Google Chrome Extensions</a> </section> <section id="license" role="complementary"> Except as otherwise <a href="https://developers.google.com/site-policies.html#restrictions">noted</a>, the content of this page is licensed under a <a href="https://creativecommons.org/licenses/by/2.5/">Creative Commons Attribution 2.5 license</a>, and examples are licensed under the <a href="https://chromium.googlesource.com/chromium/src/+/HEAD/LICENSE">BSD License</a>. </section> <section id="privacy" role="complementary"> <a href="https://policies.google.com/privacy">Privacy</a> </section> <a id="edit-this-page" href="https://edit.chromium.org/edit?repo=chromium/website/main&file=site/updates/same-site/incompatible-clients/index.md&ext_google.git=%7B%22repo%22%3A%22chromium%2Fwebsite%22%2C%22ref%22%3A%22main%22%2C%22file%22%3A%22site/updates/same-site/incompatible-clients/index.md%22%7D">Edit this page</a> </nav> <main> <div class="breadcrumbs"> <a href="/updates">updates</a> > <a href="/updates/same-site">SameSite Updates</a> > </div> <h1>SameSite=None: Known Incompatible Clients</h1> <p>Last updated: Nov 18, 2019</p> <p>Some user agents are known to be incompatible with the `SameSite=None` attribute.</p> <ul> <li>Versions of Chrome from Chrome 51 to Chrome 66 (inclusive on both ends). These Chrome versions will reject a cookie with `SameSite=None`. This also affects older versions of <a href="https://en.wikipedia.org/wiki/Chromium_(web_browser)#Browsers_based_on_Chromium">Chromium-derived browsers</a>, as well as Android WebView. This behavior was correct according to the version of the cookie specification at that time, but with the addition of the new "None" value to the specification, this behavior has been updated in Chrome 67 and newer. (Prior to Chrome 51, the SameSite attribute was ignored entirely and all cookies were treated as if they were `SameSite=None`.)</li> <li>Versions of UC Browser on Android prior to version 12.13.2. Older versions will reject a cookie with `SameSite=None`. This behavior was correct according to the version of the cookie specification at that time, but with the addition of the new "None" value to the specification, this behavior has been updated in newer versions of UC Browser.</li> <li>Versions of Safari and embedded browsers on MacOS 10.14 and all browsers on iOS 12. These versions will erroneously treat cookies marked with `SameSite=None` as if they were marked `SameSite=Strict`. This <a href="https://bugs.webkit.org/show_bug.cgi?id=198181">bug</a> has been fixed on newer versions of iOS and MacOS.</li> </ul> <p>Here is a potential approach to working around incompatible clients (in pseudocode). If you implement this sample, we highly encourage you to do your own testing to ensure that your implementation is working as intended. Note: The sample regular expression patterns below may not be perfect, as User-Agent strings can vary widely; we encourage you to use a tested User-Agent parsing library if possible.</p> <p>// Copyright 2019 Google LLC. // SPDX-License-Identifier: Apache-2.0 // Don鈥檛 send `SameSite=None` to known incompatible clients. bool shouldSendSameSiteNone(string useragent): return !isSameSiteNoneIncompatible(useragent) // Classes of browsers known to be incompatible. bool isSameSiteNoneIncompatible(string useragent): return hasWebKitSameSiteBug(useragent) || dropsUnrecognizedSameSiteCookies(useragent) bool hasWebKitSameSiteBug(string useragent): return isIosVersion(major:12, useragent) || (isMacosxVersion(major:10, minor:14, useragent) && (isSafari(useragent) || isMacEmbeddedBrowser(useragent))) bool dropsUnrecognizedSameSiteCookies(string useragent): if isUcBrowser(useragent): return !isUcBrowserVersionAtLeast(major:12, minor:13, build:2, useragent) return isChromiumBased(useragent) && isChromiumVersionAtLeast(major:51, useragent) && !isChromiumVersionAtLeast(major:67, useragent) // Regex parsing of User-Agent string. (See note above!) bool isIosVersion(int major, string useragent): string regex = "\(iP.+; CPU .*OS (\d+)[<em>\d]*.*\) AppleWebKit\/" // Extract digits from first capturing group. return useragent.regexMatch(regex)[0] == intToString(major) bool isMacosxVersion(int major, int minor, string useragent): string regex = "\(Macintosh;.*Mac OS X (\d+)</em>(\d+)[<em>\d]*.*\) AppleWebKit\/" // Extract digits from first and second capturing groups. return (useragent.regexMatch(regex)[0] == intToString(major)) && (useragent.regexMatch(regex)[1] == intToString(minor)) bool isSafari(string useragent): string safari_regex = "Version\/.* Safari\/" return useragent.regexContains(safari_regex) && !isChromiumBased(useragent) bool isMacEmbeddedBrowser(string useragent): string regex = "^Mozilla\/[\.\d]+ \(Macintosh;.*Mac OS X [</em>\d]+\) " + "AppleWebKit\/[\.\d]+ \(KHTML, like Gecko\)$" return useragent.regexContains(regex) bool isChromiumBased(string useragent): string regex = "Chrom(e|ium)" return useragent.regexContains(regex) bool isChromiumVersionAtLeast(int major, string useragent): string regex = "Chrom[^ \/]+\/(\d+)[\.\d]* " // Extract digits from first capturing group. int version = stringToInt(useragent.regexMatch(regex)[0]) return version >= major bool isUcBrowser(string useragent): string regex = "UCBrowser\/" return useragent.regexContains(regex) bool isUcBrowserVersionAtLeast(int major, int minor, int build, string useragent): string regex = "UCBrowser\/(\d+)\.(\d+)\.(\d+)[\.\d]* " // Extract digits from three capturing groups. int major_version = stringToInt(useragent.regexMatch(regex)[0]) int minor_version = stringToInt(useragent.regexMatch(regex)[1]) int build_version = stringToInt(useragent.regexMatch(regex)[2]) if major_version != major: return major_version > major if minor_version != minor: return minor_version > minor return build_version >= build</p> </main> </div> <script> // Configure Algolia search. let s = document.createElement('script'); s.src = '/_scripts/@docsearch/index.js'; document.head.append(s); window.addEventListener('load', () => { // Add the Algolia search widget. docsearch({ container: '#search', appId: 'RZDQYCCABX', apiKey: '98b0eabafeb13fe3e1af693d5713d8b4', indexName: 'chromium' }); }); // Configure Google Universal Analytics (the predecessor to GA4, we should // delete this when we don't need it any more). s = document.createElement('script'); s.src = 'https://www.googletagmanager.com/gtag/js?id=UA-5484340-1' s.async = true; document.head.append(s) window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-5484340-1'); // Configure consent bar. s = document.createElement('script'); s.src = 'https://www.gstatic.com/brandstudio/kato/cookie_choice_component/cookie_consent_bar.v3.js' s.dataset.autoloadCookieConsentBar = true; s.dataset.autoloadCookieContentBarIntlCode = ''; document.head.append(s);</script>