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 / directiveWhat 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-origin without 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.

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