X-Frame-Options Reference
DENY, SAMEORIGIN, ALLOW-FROM -- what each directive does, browser support, and how to set the header on your stack.
X-Frame-Options is an HTTP response header that tells a browser whether a page may be rendered inside a <frame>, <iframe>, <embed>, or <object>. It is the original defense against clickjacking, introduced by Microsoft in Internet Explorer 8. The header has since been obsoleted by the Content Security Policy frame-ancestors directive, but it remains widely used and widely supported. For most sites, shipping both headers together is still a reasonable baseline.
The header accepts a single directive value. Two of them work everywhere. The third does not work at all in any modern browser. Each is documented below.
DENY
DENY blocks all framing. The page cannot be embedded by any other page, including pages on your own origin. Per MDN, the browser refuses to render the document in a frame regardless of who is attempting to frame it.
X-Frame-Options: DENYThis is the correct choice for most sites. If a page has no legitimate reason to appear inside an iframe -- which is true of the vast majority of pages -- DENY is the strictest and safest setting.
SAMEORIGIN
SAMEORIGIN allows framing only by pages served from the same origin as the framed page. A page can embed itself; a page on any other origin cannot embed it.
X-Frame-Options: SAMEORIGINChoose this when your own application frames its own pages -- dashboards embedding widgets, live previews inside an editor, page builders that render your content in an iframe.
Note that subdomains are different origins: https://app.example.com and https://admin.example.com cannot frame each other under SAMEORIGIN. If you need cross-subdomain framing, use CSP frame-ancestors with explicit origins instead.
ALLOW-FROM
ALLOW-FROM was intended to allow framing by a single specified origin, so that one trusted partner site could embed your page while everyone else was blocked.
X-Frame-Options: ALLOW-FROM https://trusted.example.comDo not use this directive. It is obsolete. It was never supported in Chrome or Safari, and Firefox dropped support in version 70 (October 2019). Per MDN, modern browsers that encounter a response header with this directive will ignore the header completely -- meaning a page with ALLOW-FROM ends up with no framing protection at all.
ALLOW-FROM is worse than shipping nothing. Use CSP frame-ancestors https://partner.com to allow specific origins.Browser support
| Browser | DENY / SAMEORIGIN | ALLOW-FROM |
|---|---|---|
| Chrome | Yes | Never supported |
| Firefox | Yes | Dropped in v70 |
| Safari | Yes | Never supported |
| Edge | Yes | Never supported |
| Internet Explorer | IE8+ | IE8+ (legacy only) |
Source: MDN and CanIUse, July 2026. DENY and SAMEORIGIN are supported everywhere the header exists. ALLOW-FROM is not usable in any current browser. Internet Explorer supported ALLOW-FROM from IE8 until its end of life, but IE is no longer a supported browser.
How to set X-Frame-Options
The header must be set on the HTTP response by your server, CDN, or application framework. The one-liners below get you started; each links to a full configuration guide where available.
| Platform | Snippet |
|---|---|
| NGINX | add_header X-Frame-Options "DENY" always; |
| Apache | Header always set X-Frame-Options "DENY" |
| Cloudflare | Transform Rules → Set X-Frame-Options: DENY |
| WordPress | header('X-Frame-Options: DENY'); |
| Express.js | res.setHeader('X-Frame-Options', 'DENY'); |
| IIS | <add name="X-Frame-Options" value="DENY" /> |
The meta tag that does not work
A common mistake is trying to set the header from HTML with a <meta> tag:
<!-- This has no effect. Do not do this. -->
<meta http-equiv="X-Frame-Options" content="deny">X-Frame-Options is only honored when delivered as an HTTP response header. Browsers ignore it when it appears in a <meta> tag. The same is true of CSP frame-ancestors -- it also cannot be set via a meta tag. If you need framing protection, configure your server or CDN to emit real HTTP headers.
frame-ancestors: the modern replacement
X-Frame-Options has been obsoleted by the Content Security Policy frame-ancestors directive. frame-ancestors has been Baseline Widely Available since January 2018 and covers everything XFO can do, plus more:
frame-ancestors 'none'is similar to XFODENY.frame-ancestors 'self'is similar to XFOSAMEORIGIN.- It supports multiple explicit origins -- the thing
ALLOW-FROMpromised but never delivered. - It checks every ancestor in a nested frame chain, not just the immediate parent.
Content-Security-Policy: frame-ancestors 'self' https://trusted.example.comdefault-src does not fall back to frame-ancestors. You must set frame-ancestors explicitly. A policy of default-src 'self' does nothing to prevent clickjacking.
When both headers are present and the browser supports CSP, an enforced frame-ancestors directive takes precedence over X-Frame-Options. If the two policies conflict, the CSP directive wins. A Content-Security-Policy-Report-Only header does not override X-Frame-Options -- only an enforced policy does. In browsers that do not support CSP, X-Frame-Options remains the sole protection, which is why shipping both headers is still a reasonable baseline.
Shipping XFO alongside CSP as a legacy fallback is fine, provided the two agree. For most sites:
Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENYRead next
- NGINX clickjacking protection
- Apache clickjacking protection
- Cloudflare clickjacking protection
- WordPress clickjacking protection
Related: X-Frame-Options vs CSP frame-ancestors -- the sharp edges most guides skip
Related: Why JavaScript frame-busting doesn't stop clickjacking
Related: DoubleClickjacking: the attack that bypasses framing protections