Skip to content
ClickJack Test

← Guides

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>. Microsoft introduced it in Internet Explorer 8 as the first header-based defense against clickjacking. RFC 7034 (an Informational RFC, not a standard) documented it in 2013; today the HTML Living Standard defines how browsers process it. The Content Security Policy frame-ancestors directive has superseded it, but every current browser still enforces it, and sending both headers remains a sensible baseline.

The header takes one value, matched case-insensitively. DENY and SAMEORIGIN work everywhere. ALLOW-FROM works in no current browser.

DENY

DENY blocks all framing. No page can embed it, including pages on your own origin.

X-Frame-Options: DENY

This is the right choice for most sites. If a page has no reason to appear in an iframe (true of most pages), use DENY.

SAMEORIGIN

SAMEORIGIN allows framing only when every ancestor frame, up to the top-level page, has the same origin as the framed page. Pages on your origin can embed it; pages on any other origin cannot.

X-Frame-Options: SAMEORIGIN

Use it when your application frames its own pages: dashboards embedding widgets, live previews in an editor, page builders that render your content in an iframe.

The every-ancestor check is what the HTML standard requires, and what Chrome (since 61) and Firefox (since 59) do. Older browsers compared only against the top-level page, as RFC 7034 describes, so a cross-origin page framed inside your site could in turn frame your SAMEORIGIN pages.

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 meant to allow framing by one named origin, so a single trusted partner could embed your page while everyone else was blocked.

X-Frame-Options: ALLOW-FROM https://trusted.example.com

Do not use this directive. It is obsolete. Chrome, Safari and Chromium-based Edge never supported it, and Firefox removed it in version 70 (October 2019). The HTML standard says it is not to be implemented: a lone ALLOW-FROM value is treated as if the header were absent, so the page has no framing protection at all.

Never ship ALLOW-FROM. Browsers ignore the whole header, so it protects nothing while looking like protection in a config review. Use CSP frame-ancestors https://partner.com to allow specific origins.

Send exactly one value. Browsers that follow the HTML standard treat repeated headers and comma-separated values the same way: a repeated identical value (SAMEORIGIN, SAMEORIGIN) behaves like one, but any mix that includes DENY or SAMEORIGIN (DENY, SAMEORIGIN) blocks all framing. This usually happens when the app and a proxy or CDN both add the header.

Browser support

BrowserDENY / SAMEORIGINALLOW-FROM
Chrome4+Never supported
Firefox4+18 to 69; removed in 70
Safari4+Never supported
Edge12+Legacy Edge 12 to 18 only; not in Chromium Edge (79+)
Internet ExplorerIE8+IE8 to 11 (retired)

Sources: MDN browser compatibility data and Can I Use, October 2026. Mobile versions of these browsers match their desktop counterparts. Only retired browsers (Internet Explorer, legacy Edge, Firefox 69 and older) ever honored ALLOW-FROM.

How to set X-Frame-Options

Set the header on the HTTP response from your server, CDN, or application framework. Each one-liner below links to a full guide where one exists.

PlatformSnippet
NGINXadd_header X-Frame-Options "DENY" always;
ApacheHeader always set X-Frame-Options "DENY"
CloudflareRules → Response Header Transform Rule → Set static: X-Frame-Options = DENY
WordPressadd_action('send_headers', function () { header('X-Frame-Options: DENY'); });
Express.jsapp.use((req, res, next) => { res.setHeader('X-Frame-Options', 'DENY'); next(); });
IIS<add name="X-Frame-Options" value="DENY" /> inside <system.webServer><httpProtocol><customHeaders> in web.config

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 only works as an HTTP response header; browsers ignore it in a <meta> tag. CSP frame-ancestors is ignored in a meta tag too. Configure your server or CDN to send real HTTP headers.

frame-ancestors: the modern replacement

X-Frame-Options has been superseded by the Content Security Policy frame-ancestors directive, which MDN lists as Baseline Widely Available since January 2018. It covers everything XFO does, and more:

  • frame-ancestors 'none' is similar to XFO DENY.
  • frame-ancestors 'self' is similar to XFO SAMEORIGIN.
  • It supports multiple explicit origins: the thing ALLOW-FROM promised but never delivered.
  • It has always checked every ancestor in a nested frame chain. XFO SAMEORIGIN only does in modern browsers.
Content-Security-Policy: frame-ancestors 'self' https://trusted.example.com

frame-ancestors does not fall back to default-src. You must set frame-ancestors explicitly. A policy of default-src 'self' does nothing to prevent clickjacking.

If a response carries an enforced CSP with a frame-ancestors directive, browsers ignore X-Frame-Options entirely, whatever either header says. So frame-ancestors * plus X-Frame-Options: DENY leaves the page frameable by any site. A Content-Security-Policy-Report-Only header does not override X-Frame-Options; only an enforced policy does. Browsers without frame-ancestors support (Internet Explorer) rely on X-Frame-Options alone.

Keep XFO as a legacy fallback and make sure the two headers agree. For most sites:

Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY

Test your site now →

Check your headers

See whether a site sends X-Frame-Options or CSP frame-ancestors, and what browsers will do with them.