Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
SecurityMedium

External Link Security

Links that open in a new tab using target='_blank' must include rel='noopener noreferrer' to prevent the opened page from accessing the opener's window context.

Utilities
Quick take
Typical fix time 10 min
  • Always add `rel="noopener noreferrer"` to any `<a target="_blank">` link
  • `noopener` prevents the new tab from accessing `window.opener` and redirecting your page
  • `noreferrer` additionally suppresses the `Referer` header sent to the destination site
  • Modern browsers (Chrome 88+, Firefox 79+) implicitly add `noopener` for cross-origin `_blank` links, but explicit markup is required for older browsers and same-origin links
  • ESLint rule `jsx-a11y/anchor-is-valid` and `eslint-plugin-security` can enforce this automatically
Why it matters: A malicious site opened via target='_blank' can use window.opener.location to silently redirect your original tab to a phishing page — the user switches back and sees a fake login screen on what appears to be your domain.

Rule Details

When a link opens a new browser tab with target="_blank", the new page receives a reference to the opener's window object via window.opener. A malicious destination can exploit that behavior in a reverse tabnapping (opens in new tab) flow unless you explicitly add rel="noopener" (opens in new tab) or rel="noreferrer" (opens in new tab).

Code Example

1. User is on bank.example.com
2. User clicks a link to malicious.com (opens in new tab)
3. malicious.com runs: window.opener.location = 'https://fake-bank.example.com/login'
4. User switches back to the original tab
5. Tab now shows the phishing site — user thinks the bank session expired and types credentials

Why It Matters

A malicious site opened via target='_blank' can use window.opener.location to silently redirect your original tab to a phishing page — the user switches back and sees a fake login screen on what appears to be your domain.

The Fix

❌ Vulnerable to reverse tabnapping
<a href="https://example.com" target="_blank">Visit Example</a>
 
✅ Safe — no access to window.opener
<a href="https://example.com" target="_blank" rel="noopener noreferrer">Visit Example</a>

React / JSX

❌ Vulnerable
<a href="https://external.com" target="_blank">External Link</a>
 
✅ Safe
<a href="https://external.com" target="_blank" rel="noopener noreferrer">
  External Link
</a>

Understanding the rel Values

ValueWhat it does
noopenerSets window.opener to null in the new tab — the opened page cannot access the original window
noreferrerSuppresses the Referer HTTP header sent to the destination; also implies noopener
Both togetherMaximum privacy + security: no opener access, no referrer sent

Recommendation: Use both noopener noreferrer for external links. Use noopener alone if you need the referrer for analytics on same-origin links.

Browser Behavior

As of Chrome 88 and Firefox 79, cross-origin target="_blank" links implicitly get noopener behavior. However:

  • Same-origin _blank links are not implicitly protected
  • Older browsers still require the explicit attribute
  • noreferrer is never added implicitly

For these reasons, always add the attribute explicitly.

Automated Enforcement

ESLint (React projects)

pnpm add -D eslint-plugin-react
// .eslintrc.json
{
  "rules": {
    "react/jsx-no-target-blank": ["error", {
      "allowReferrer": false,
      "enforceDynamicLinks": "error"
    }]
  }
}

Finding Violations in Existing Code

# Search HTML files
grep -rn 'target="_blank"' ./src --include="*.html" | grep -v 'new-tab'
 
# Search JSX/TSX files
grep -rn 'target="_blank"' ./src --include="*.jsx" --include="*.tsx" | grep -v 'new-tab'

Internal links (same domain) are generally safe to open in new tabs without noopener because you control both pages. However, adding it is still a best practice for consistency:

<!-- Internal link — window.opener risk is lower, but still good practice -->
<a href="/docs/guide" target="_blank" rel="noopener">Open Docs</a>
When to Break This Rule

If you intentionally need the opened page to communicate back with the opener via window.opener (e.g., OAuth popup flows), omit noopener. This is a legitimate use case — document it clearly in code comments.

Exceptions

  • A weaker form control is only acceptable when the business requirement and compensating controls are documented explicitly.
  • If the flow is already transport-insecure, inaccessible, or externally embedded in a way that changes the threat model, fix that stronger issue first.
  • False positives are common on demo, sandbox, or intentionally constrained flows, but they should still be bounded and clearly labeled.

Verification

Automated Checks

  • Test the affected flow in a production-like environment, not just local development.
  • Document any intentional exceptions explicitly.

Manual Checks

  • Inspect the final HTTP response or browser behavior to confirm the control is actually enforced.
  • Verify third-party integrations or embeds still work after the restriction is applied.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Find all anchor elements with target='_blank' in the HTML and JSX source. Verify each one includes rel='noopener noreferrer' (or at minimum rel='noopener'). Flag any external links opening in a new tab that are missing this attribute.

Fix

Auto-fix issues

Add rel='noopener noreferrer' to all <a target='_blank'> elements. In React/JSX this is rel="noopener noreferrer". Configure an ESLint rule to prevent this from recurring.

Explain

Learn more

Explain the reverse tabnapping attack enabled by window.opener, how rel='noopener' and rel='noreferrer' prevent it, and the difference between the two values.

Review

Code review

Review server config, headers, forms, and integration points related to External Link Security. Flag exact responses, cookies, or browser behaviors that violate the rule, and verify them against the effective production-like response.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Mozilla Observatory
observatory.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Set a Referrer-Policy header

The Referrer-Policy header controls how much referrer information is sent when navigating from your site to another, protecting user privacy and preventing leaking sensitive URL parameters.

Security
Set an X-Frame-Options header

The X-Frame-Options header controls whether your page can be embedded in an iframe, frame, or object — preventing clickjacking attacks.

Security
Set an HSTS header

The Strict-Transport-Security response header tells browsers to always use HTTPS for your domain, preventing protocol downgrade attacks and cookie hijacking.

Security

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385