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

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.

Utilities
Quick take
Typical fix time 5 min
  • Use `X-Frame-Options: DENY` to prevent all framing, or `SAMEORIGIN` to allow framing only from your own domain
  • `ALLOWFROM` is obsolete and unsupported in modern browsers — use CSP `frame-ancestors` instead
  • The modern equivalent is `Content-Security-Policy: frame-ancestors 'none'` — prefer CSP for new sites
  • Both headers can coexist: X-Frame-Options for older browsers, frame-ancestors for modern ones
  • Clickjacking attacks trick users into clicking invisible iframe buttons — DENY eliminates this entirely
Why it matters: Without framing protection, an attacker can embed your banking login page in a transparent iframe on a malicious site and trick users into clicking buttons they cannot see — transferring money, changing settings, or leaking credentials.

Rule Details

Clickjacking (opens in new tab), also called UI redressing, is an attack where a malicious page embeds your application in a hidden or transparent iframe and tricks users into clicking elements they cannot see. X-Frame-Options (opens in new tab) prevents your page from being embedded as an iframe.

Code Example

┌─────────────────────────────────────┐
│  attacker.com (visible page)        │
│                                     │
│  "Click here to win a prize!"       │
│                                     │
│  ┌─────────────────────────────┐    │  ← Invisible iframe
│  │  bank.com (opacity: 0.0)   │    │    positioned over the button
│  │  [Transfer $1000] button   │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘

The user clicks "win a prize" but actually clicks the hidden "Transfer $1000" button on the bank's page.

Why It Matters

Without framing protection, an attacker can embed your banking login page in a transparent iframe on a malicious site and trick users into clicking buttons they cannot see — transferring money, changing settings, or leaking credentials.

X-Frame-Options Values

ValueBehavior
DENYPage cannot be embedded in any frame, regardless of origin
SAMEORIGINPage can only be embedded by a frame on the same origin
ALLOW-FROM uriObsolete — unsupported in Chrome, Firefox; use CSP frame-ancestors

Most sites — use DENY

X-Frame-Options: DENY

Use this when your pages should never appear inside an iframe (logins, dashboards, payment pages).

Sites with same-origin embeds

X-Frame-Options: SAMEORIGIN

Use this if your own domain embeds the page (e.g., an admin panel loading pages in an internal iframe).

Server Configuration

Nginx

add_header X-Frame-Options "DENY" always;

Apache

<IfModule mod_headers.c>
    Header always set X-Frame-Options "DENY"
</IfModule>

Next.js

// next.config.js
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
        ],
      },
    ]
  },
}

Express.js (using Helmet)

import helmet from 'helmet'
 
app.use(
  helmet.frameguard({
    action: 'deny',  // or 'sameorigin'
  })
)

Modern Alternative: CSP frame-ancestors

The Content-Security-Policy: frame-ancestors (opens in new tab) directive supersedes X-Frame-Options and is more flexible:

# Equivalent to DENY
Content-Security-Policy: frame-ancestors 'none'
 
# Equivalent to SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
 
# Allow specific trusted domains (not possible with X-Frame-Options)
Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com

frame-ancestors takes precedence over X-Frame-Options in browsers that support both. For maximum compatibility, set both:

add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none'" always;

Exceptions

  • A missing or weak header should be evaluated against the live production response path, not only the framework or server config in isolation.
  • Legacy integrations or embedded third-party content may require narrowly scoped exceptions, but they should be documented explicitly instead of left permissive by default.
  • When multiple security headers are missing, prioritize the header that removes the highest exploitability or browser capability first.

Standards

  • Align the implementation with MDN: X-Frame-Options and verify the effective response or browser behavior, not only the configuration file.
  • Align the implementation with OWASP: Clickjacking Defense Cheat Sheet and verify the effective response or browser behavior, not only the configuration file.
  • Align the implementation with MDN: CSP frame-ancestors and verify the effective response or browser behavior, not only the configuration file.

Verification

Automated Checks

  • Inspect the effective response headers with curl, a security header scanner, or equivalent tooling against representative live responses.

Manual Checks

  • Payment widgets (Stripe, PayPal) embedded in merchants' sites
  • Analytics dashboards embedded in CMS admin panels
  • Third-party video players

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

Check whether the server sends an X-Frame-Options header (DENY or SAMEORIGIN) or a Content-Security-Policy header with frame-ancestors directive to prevent clickjacking.

Fix

Auto-fix issues

Add X-Frame-Options: DENY to all responses if the site does not need to be embedded anywhere. If legitimate framing is needed on the same origin, use SAMEORIGIN. For fine-grained control, use CSP frame-ancestors instead.

Explain

Learn more

Explain what a clickjacking attack is, how X-Frame-Options and CSP frame-ancestors prevent it, and the difference between DENY and SAMEORIGIN values.

Review

Code review

Review server config, headers, forms, and integration points related to Set an X-Frame-Options header. 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.

Implement a content security policy

A Content Security Policy is implemented to prevent XSS attacks and control resource loading.

Security
Set X-Content-Type-Options: nosniff

The X-Content-Type-Options: nosniff header prevents browsers from MIME-sniffing a response away from the declared Content-Type, blocking a class of drive-by download and XSS 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