Why rel='noopener' Is Obsolete for Links
Why rel=noopener is obsolete for target=_blank links, what reverse tabnabbing was, and when noreferrer, opener, or COOP still matter.
For a plain target="_blank" link, rel="noopener" is now redundant: every current version of Chrome, Edge, Firefox, and Safari applies noopener behavior automatically, so a bare target="_blank" already sets window.opener to null.
If you still type it out of muscle memory, or watch the linter flag the one anchor you forgot, you are patching a hole the browser closed years ago. This article explains the vulnerability it was meant to prevent, when browsers made the fix default, and the precise cases where rel still does real work: noreferrer (not automatic), rel="opener" (opt back in), and the Cross-Origin-Opener-Policy header for site-wide control.
Key Takeaways
- On modern browsers, a bare
target="_blank"already nullswindow.opener, so hand-addingrel="noopener"is defense-in-depth for a hole the browser already closed. - Implicit
noopenershipped in stages (Safari in 2018–19, Firefox 79 in mid-2020, Chromium 88 in early 2021) and is now part of the WHATWG HTML standard. - Implicit
noopenercovers roughly 95% of global browser usage, per caniuse.com. noreferreris not implicit: it still strips theRefererheader and also impliesnoopener, so add it only when you want referrer privacy.- Use
rel="opener"to opt back intowindow.opener, andCross-Origin-Opener-Policy: same-originto sever opener sharing across a whole document in one place.
The original problem: reverse tabnabbing
Before browsers changed the default, a target="_blank" link handed the newly opened page a live reference back to the page that opened it. Reverse tabnabbing is the attack that exploits this: the destination page reads window.opener and redirects the original tab to a phishing clone while the user is focused on the new tab. Mathias Bynens’ canonical explainer on the problem sums it up plainly: wherever window.opener exists, the opened page can steer the opener somewhere else, whatever origin either page belongs to.
The exploit is a one-liner running in the opened document:
if (window.opener) {
window.opener.location = 'https://you-re-hacked.com';
}
The critical detail is that this works across origins. Reading and writing window.opener.location is not blocked when the two pages come from different hosts, so neither the same-origin policy nor CORS stands in the way of redirecting the opener. That made it dangerous anywhere you rendered user-generated or third-party links (forums, comments, profile fields) where an attacker controls the href.
What changed: target=“_blank” now implies rel=“noopener”
Discover how at OpenReplay.com.
Browsers fixed the default. On <a>, <area>, and <form> elements, a target="_blank" now carries the same effect as writing rel="noopener" yourself: the opened document gets null back from window.opener, with no attribute required. That behavior is written into the WHATWG HTML specification, whose rules for following a hyperlink treat any _blank target as noopener unless the link opts out with rel="opener". OWASP now points readers at that same standardized default and treats the attack as largely closed on evergreen browsers.
The change rolled out over roughly three years, so “modern browsers do this” is a timeline, not a single date:
| Engine | First stable version with implicit noopener | Approx. ship date |
|---|---|---|
| Safari / WebKit | Safari 12.1 (preview in Tech Preview 68) | Late 2018 – 2019 |
| Firefox / Gecko | Firefox 79 | Mid-2020 |
| Chromium (Chrome, Edge) | Chrome/Edge 88 | Early 2021 |
Note these versions describe implicit behavior, not when the rel="noopener" attribute itself became supported. That landed years earlier and is a different milestone. Caniuse’s implicit-noopener table puts global support at roughly 95%, with evergreen browsers covered since around 2018. The remaining slice is small but real, so check your own analytics before you strip the attribute out. The notable holdout is legacy non-Chromium Edge.
Obsolete to hand-add is not the same as useless
rel="noopener" being redundant to type does not make the whole rel attribute pointless. The keyword that’s now automatic is noopener specifically. The others still change behavior:
| Keyword | What it does | Still needed to type in 2026? |
|---|---|---|
noopener | Nulls window.opener in the opened page | No, implicit on target="_blank" |
noreferrer | Strips the Referer header and implies noopener | Only when you want referrer privacy |
opener | Restores window.opener (opts back in) | Yes, when you genuinely need the reference |
noreferrer is not implicit. It still suppresses the Referer header, so add it only when you actually want to withhold the originating URL from the destination. It also carries the security benefit for free: because noreferrer nulls the opener as well, adding noopener alongside it buys nothing. That makes the common rel="noopener noreferrer" pairing doubly redundant on modern browsers, since noreferrer on its own covers both concerns.
If you genuinely need the opened page to keep its window.opener reference (a popup that posts a message back, say), opt in explicitly with rel="opener". The WebKit release notes that introduced the change describe it the same way: the secure behavior is now the default, and rel="opener" is how you deliberately reverse it.
One honest caveat for legacy support: adding rel="noopener" anyway is harmless. Chrome’s Lighthouse audit docs note that spelling the attribute out still buys some cover for anyone stuck on an older engine such as Edge Legacy. It’s noise on modern browsers, but it isn’t wrong.
COOP: the scalable, site-wide control
To sever window.opener sharing across an entire document in one place, send the Cross-Origin-Opener-Policy: same-origin response header instead of decorating every link. The Cross-Origin-Opener-Policy (COOP) header decides whether a newly opened top-level document joins your browsing context group or gets one of its own. Under same-origin, cross-origin documents land in a separate group and the references between them and their opener are cut, which closes the opener channel once, centrally, rather than per-anchor.
# nginx
add_header Cross-Origin-Opener-Policy "same-origin";
// Express
app.use((req, res, next) => {
res.set('Cross-Origin-Opener-Policy', 'same-origin');
next();
});
One constraint: COOP is delivered only as an HTTP response header. There is no <meta http-equiv> equivalent, so if you can’t set response headers on your infrastructure, you can’t apply COOP. It’s widely supported in current browsers and worth enabling as defense-in-depth. A session replay of a real user clicking an external target="_blank" link is a practical way to confirm the original tab was never navigated, and to reproduce any unexpected-navigation report against the exact browser the user was on.
The verdict: what to do now
For new code targeting modern browsers, don’t hand-add rel="noopener"; the browser sets it for you. You can safely relax lint rules that force it on every target="_blank", such as react/jsx-no-target-blank; keep the rule enabled only if you must cover Edge Legacy or other pre-2021 engines. Add rel="noreferrer" when, and only when, you want to suppress the Referer header. Use rel="opener" in the rare case you need the opener reference back. For a scalable, document-wide guarantee, send Cross-Origin-Opener-Policy: same-origin.
Worth flagging: earlier guidance, including OpenReplay’s own older post recommending rel="noreferrer noopener" on every link, treats noopener as something you must always type and doesn’t account for the implicit default. That reflects a habit the industry held onto long after the browsers moved. The accurate 2026 position is narrower: noopener is now the default, so hand-adding it is obsolete, while noreferrer, rel="opener", and COOP each still do a distinct job. Reach for those deliberately, and let the browser handle the rest.
FAQs
Does rel='noreferrer' include rel='noopener'?
Yes. Setting rel='noreferrer' implies rel='noopener' automatically, so it nulls window.opener in addition to stripping the Referer header. This means the common rel='noopener noreferrer' pairing is doubly redundant on modern browsers: noreferrer alone covers both the opener nulling and the referrer suppression. Add noreferrer only when you actually want to withhold the originating URL from the destination page.
What happens if I omit rel='noopener' entirely on a target='_blank' link today?
Nothing dangerous on modern browsers. A bare target='_blank' already sets window.opener to null because Chrome, Edge, Firefox, and Safari apply noopener behavior implicitly, a rule codified in the WHATWG HTML standard. Caniuse puts that at roughly 95% of global browser usage. Reverse tabnabbing is inert by default; the only gap is legacy engines like non-Chromium Edge Legacy.
Can I set Cross-Origin-Opener-Policy using a meta tag instead of a header?
No. COOP is delivered only as an HTTP response header, and there is no meta http-equiv equivalent. If your infrastructure cannot set response headers, you cannot apply COOP and must fall back to per-link rel attributes or the implicit noopener default. When you can set headers, sending Cross-Origin-Opener-Policy: same-origin severs window.opener sharing across an entire document in one central place rather than decorating every anchor.
How do I opt back into window.opener when I actually need it?
Use rel='opener' explicitly on the link. Because the secure behavior of nulling window.opener is now the browser default, rel='opener' is how you deliberately restore the opener reference, for example when a popup needs to post a message back to the page that opened it. This reverses the implicit noopener behavior for that specific link without affecting others.