Cross-Origin-Opener-Policy (COOP)
Puts your page in its own browsing context group, so cross-origin windows you open, or that open you, lose their reference to it.
Updated 3 November 2026
Check your site's COOP header
We send one normal request (following redirects) and grade the Cross-Origin-Opener-Policy header on the final response.
Recommended value
Cross-Origin-Opener-Policy: same-origin-allow-popups
| Value / directive | What it does |
|---|---|
| unsafe-none | Default. Cross-origin windows keep window.opener references. |
| same-origin-allow-popups | Isolates you from cross-origin pages that open you, but keeps references to popups you open. Safe with OAuth and payment popups. |
| same-origin | Full isolation from every cross-origin window. Required (with COEP) for cross-origin isolation. |
What COOP protects against
If a malicious site opens your page with window.open(), or your page is opened from a link, the two windows can hold references to each other. With those references an attacker can count your frames, navigate your window, or measure timing to infer state ("XS-Leaks"). It is also part of the Spectre mitigation story: pages in the same browsing context group can end up in the same process.
Cross-Origin-Opener-Policy: same-origin cuts those references for any cross-origin window.
Popups and sign-in flows
Full same-origin breaks flows where you open a cross-origin popup and wait for it to post a message back, such as "Sign in with Google" popups or some payment providers. same-origin-allow-popups keeps those working while still protecting you from windows that open you. Test sign-in and checkout after enabling it.
Cross-origin isolation
COOP: same-origin plus Cross-Origin-Embedder-Policy: require-corp (or credentialless) makes the page cross-origin isolated, which unlocks SharedArrayBuffer and high-resolution timers. Most sites do not need this.
How to set Cross-Origin-Opener-Policy
nginx
# inside your server { } block
add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always;
Apache
# .htaccess or virtual host (needs mod_headers)
Header always set Cross-Origin-Opener-Policy "same-origin-allow-popups"
Laravel middleware
// app/Http/Middleware/SecurityHeaders.php
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('Cross-Origin-Opener-Policy', 'same-origin-allow-popups');
return $response;
}
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\App\Http\Middleware\SecurityHeaders::class);
})
Express (Node.js)
app.use((req, res, next) => {
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin-allow-popups');
next();
});
// or: app.use(require('helmet')()) for a sensible default set
Cloudflare
Rules → Transform Rules → Modify Response Header → Create rule.
Match: all incoming requests (or your hostname).
Then: Set static → Header name: Cross-Origin-Opener-Policy → Value: same-origin-allow-popups
In nginx, an add_header inside a location block discards every add_header inherited from the server block. Repeat your security headers there, or use an include file. The always flag also sends the header on error responses.
Common mistakes
-
Setting
same-originwithout testing OAuth or payment popups. - Expecting COOP alone to enable SharedArrayBuffer. It also needs COEP.
FAQ
Should every site set Cross-Origin-Opener-Policy?
It is a cheap hardening step. same-origin-allow-popups is a safe starting value for most apps.
Does COOP break Google sign-in popups?
same-origin can. same-origin-allow-popups keeps popups you open working.
Related guides
Headers are one of 30+ things attackers check
The free StackShield scan also checks for exposed .env files, debug mode, leaked tooling, weak TLS and more, from the outside, in about a minute.
Run a free scan