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.
- 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
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
| Value | Behavior |
|---|---|
DENY | Page cannot be embedded in any frame, regardless of origin |
SAMEORIGIN | Page can only be embedded by a frame on the same origin |
ALLOW-FROM uri | Obsolete — unsupported in Chrome, Firefox; use CSP frame-ancestors |
Recommended Configuration
Most sites — use DENY
X-Frame-Options: DENYUse this when your pages should never appear inside an iframe (logins, dashboards, payment pages).
Sites with same-origin embeds
X-Frame-Options: SAMEORIGINUse 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.comframe-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.