How Safari Enforces CORS and COOP Security Headers
This article provides a direct overview of how Apple’s Safari web browser, powered by the WebKit engine, enforces Cross-Origin Resource Sharing (CORS) and Cross-Origin-Opener-Policy (COOP) headers. It breaks down the exact mechanisms Safari uses to validate cross-origin network requests through CORS, explains how the browser isolates top-level documents using COOP to prevent side-channel attacks, and highlights WebKit-specific nuances developers must handle when configuring these security policies.
Cross-Origin Resource Sharing (CORS) in Safari
Safari uses the WebKit layout engine to enforce the Same-Origin Policy (SOP) by default, which prevents a document or script loaded from one origin from interacting with resources from another origin. To allow legitimate cross-origin network requests, Safari relies on the strict evaluation of CORS headers.
1. Simple Requests
When Safari makes a "simple" cross-origin request (such as a basic
GET or POST request with standard headers), it
automatically appends the Origin header to the outgoing
request.
When the server responds, Safari checks for the
Access-Control-Allow-Origin header:
- Match: If the header value matches the requesting
origin (or is a wildcard
*for non-credentialed requests), Safari passes the response data to the JavaScript environment. - Mismatch or Missing: If the header is missing or does not match, Safari blocks the JavaScript code from reading the response and logs a CORS error in the Web Inspector console, even though the network request itself succeeded at the HTTP level.
2. Preflight Requests
For complex operations—such as requests using PUT or
DELETE methods, application/json content
types, or custom headers—Safari enforces a mandatory preflight
check.
- Before sending the actual request, Safari dispatches an
OPTIONSrequest to the server. - This request includes
Access-Control-Request-MethodandAccess-Control-Request-Headers. - Safari waits for a
2xxstatus code response containing the correspondingAccess-Control-Allow-MethodsandAccess-Control-Allow-Headers. - If the preflight fails or attempts an HTTP redirect, Safari immediately aborts the actual request.
3. Credentials and Intelligent Tracking Prevention (ITP)
When a fetch or XMLHttpRequest includes credentials (cookies, authorization headers, or TLS client certificates), Safari enforces stricter CORS rules:
- The server must respond with
Access-Control-Allow-Credentials: true. - Safari strictly forbids the use of the
*wildcard in theAccess-Control-Allow-Originheader when credentials are included. The exact origin must be echoed back. - The ITP Factor: Even if CORS headers are configured correctly for credentials, Safari’s Intelligent Tracking Prevention (ITP) blocks third-party cookies by default. Therefore, cross-origin credentialed requests will not send cookies in Safari unless the user has explicitly granted permission via the Storage Access API.
4. Safari-Specific CORS Strictness
Safari is notably stricter than other browsers regarding how it caches and processes certain CORS requests:
- Media Elements: Safari strictly requires CORS
headers on
<video>and<audio>elements if they are drawn into a<canvas>or manipulated via the Web Audio API. Without thecrossoriginattribute on the HTML tag and the corresponding server headers, Safari taints the canvas and blocks data extraction. - Vary Header: If a server dynamically generates the
Access-Control-Allow-Originheader based on the request, Safari requires the server to includeVary: Originin the response. Without this, Safari may serve a cached response with the wrong CORS header to a different origin, resulting in a failure.
Cross-Origin-Opener-Policy (COOP) in Safari
Introduced to Safari in version 15.2, the Cross-Origin-Opener-Policy
(COOP) header is enforced by WebKit to provide process isolation for
top-level documents. This prevents malicious cross-origin pages from
exploiting side-channel attacks (like Spectre) or abusing the
window.opener object.
1. Managing Browsing Context Groups
When a website opens a new window or tab using
window.open() or an anchor tag with
target="_blank", the two windows normally share a "browsing
context group." This allows them to communicate synchronously via the
window.opener DOM property. Safari enforces COOP by
breaking this link and placing documents into separate browsing context
groups (and often separate operating system processes) based on the
header's value.
2. Enforcement of COOP Values
Safari only evaluates the COOP header on top-level documents; it completely ignores the header if it is present on iframes or subresources. Safari enforces the three standard COOP directives as follows:
unsafe-none(Default): Safari takes no action. The document stays in the same browsing context group as its opener, unless the opener itself has a strict COOP policy.same-origin-allow-popups: Safari isolates the current document from any cross-origin window that opened it. However, if the current document opens a new popup usingwindow.open(), Safari allows them to stay in the same group unless that new popup explicitly enforces its own COOP policy ofsame-origin.same-origin: This is the strictest enforcement. Safari places the document into a dedicated browsing context group. It can only share this group with other documents that are from the exact same origin and also declare asame-originCOOP header. If asame-origindocument opens a cross-origin popup, Safari setswindow.openertonullin the popup, and the opener loses the ability to control or inspect the popup reference.
3. Enabling Cross-Origin Isolation
Safari uses COOP enforcement as a prerequisite for enabling powerful
web features that pose security risks in shared memory environments,
specifically SharedArrayBuffer and high-resolution timers
(performance.now()).
To unlock these features, Safari requires the document to be in a "cross-origin isolated" state. Safari only grants this state if the server sends both of the following headers on the main document:
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp(orcredentialless)
When Safari detects both headers, it isolates the page's process from
other origins via COOP, ensures all subresources opt-in to being loaded
via COEP (Cross-Origin-Embedder-Policy) or CORS, and subsequently sets
the window.crossOriginIsolated property to
true. If either header is missing or misconfigured, Safari
blocks access to SharedArrayBuffer.