The Cross-Origin-Opener-Policy (COOP) header controls whether a top-level page shares a browsing context group with cross-origin pages that open it (or that it opens) via window.open() or links. Without it, a malicious page can keep a live window reference to your site and probe it for cross-site leaks (XS-Leaks) or redirect it in tabnabbing attacks. This guide shows how to set the header across all major web servers and platforms.
Choosing a Policy Value
| Value | Behaviour | Best For |
|---|---|---|
unsafe-none | Default — page shares its browsing context group with cross-origin openers | Legacy behaviour; no protection |
same-origin | Page is isolated from any cross-origin opener or openee — the window reference between them is severed | Recommended for most sites — full isolation against XS-Leaks and tabnabbing |
same-origin-allow-popups | Page is isolated from its opener, but popups it opens without COOP keep a reference back | Sites that open third-party popups (OAuth flows, payment windows) |
noopener-allow-popups | Severs the opener reference in both directions but does not require same-origin popups | Newer value with limited browser support — verify before relying on it |
same-origin is the right choice for most sites. If your site uses OAuth or payment popups that must communicate with the opener window, use same-origin-allow-popups instead — plain same-origin will break those flows.Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp makes your page "cross-origin isolated", unlocking powerful APIs like SharedArrayBuffer and high-resolution timers. COOP alone is still valuable as a defence-in-depth security header.Nginx
server {
listen 443 ssl;
server_name example.com;
add_header Cross-Origin-Opener-Policy "same-origin" always;
# ... rest of config
}sudo nginx -t && sudo systemctl reload nginxApache
<VirtualHost *:443>
<IfModule mod_headers.c>
Header always set Cross-Origin-Opener-Policy "same-origin"
</IfModule>
</VirtualHost>Or in .htaccess:
<IfModule mod_headers.c>
Header always set Cross-Origin-Opener-Policy "same-origin"
</IfModule>sudo a2enmod headers && sudo systemctl reload apache2IIS (web.config)
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cross-Origin-Opener-Policy" value="same-origin" />
</customHeaders>
</httpProtocol>
</system.webServer>Caddy
example.com {
header Cross-Origin-Opener-Policy "same-origin"
# ... rest of config
}Cloudflare (Transform Rules)
- Go to Rules → Transform Rules → Modify Response Header.
- Add a rule with Operation: Set, Header name: Cross-Origin-Opener-Policy, Value: same-origin.
- Deploy the rule.
Next.js
// next.config.mjs
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Cross-Origin-Opener-Policy',
value: 'same-origin',
},
],
},
];
},
};
export default nextConfig;Watch Out for Popup-Based Flows
The most common breakage after enabling COOP is a login or checkout popup that can no longer talk back to the page that opened it. If your site uses window.open() together with window.opener or postMessage to a popup (Google/Facebook sign-in, Stripe Checkout in popup mode, OAuth flows), use same-origin-allow-popups instead of same-origin and re-test those flows before deploying.
Verifying the Header
curl -I https://example.com | grep -i cross-origin-opener
# Expected: cross-origin-opener-policy: same-originOr use the ShowDNS Security Headers Scanner. You can also confirm isolation in the browser console — on an isolated page, window.crossOriginIsolated returns true when COEP is set too.