← Docs

CSP frame-ancestors Reference

The Content Security Policy directive that controls who can embed your pages -- syntax, browser support, and the sharp edges most guides skip.

frame-ancestors is a Content Security Policy directive that controls which origins may embed your page in a <frame>, <iframe>, <object>, or <embed>. It was introduced in CSP Level 2 and has been Baseline Widely Available since January 2018. It is the modern replacement for X-Frame-Options, covering everything that header can do and several things it cannot.

Unlike X-Frame-Options -- which can only express one of three fixed values -- frame-ancestors supports the full CSP source list syntax, including multiple origins and wildcard subdomains. It also checks every ancestor in a nested frame chain, not just the immediate parent.

Syntax

frame-ancestors is delivered as part of the Content-Security-Policy HTTP response header. It accepts two forms:

Block everything

Content-Security-Policy: frame-ancestors 'none'

No origin may embed this page. The single quotes around none are mandatory. This is equivalent to X-Frame-Options: DENY.

Allow specific origins

Content-Security-Policy: frame-ancestors 'self' https://example.com

The page may be embedded by its own origin (self) and by https://example.com. All other origins are blocked. Multiple origins are space-separated. This is the thing X-Frame-Options: ALLOW-FROM promised but never delivered: specifying exactly which external sites may frame you.

The directive supports the same source expressions as other CSP fetch directives: scheme-only (https:), host-only (example.com), and wildcard subdomains (*.example.com).

The wildcard gotcha

CSP source expressions support wildcards -- but they do not always work the way you expect.

A leading wildcard does not match the bare domain. *.example.com matches sub.example.com but does not match example.com itself. If you need both the root domain and all subdomains, list both:

Content-Security-Policy: frame-ancestors 'self' example.com *.example.com

A bare asterisk allows everything. frame-ancestors * is valid CSP and permits framing by any origin. It is the opposite of frame-ancestors 'none'. Security scanners that flag "CSP: Wildcard Directive" on frame-ancestors are correct: a wildcard in this directive provides zero clickjacking protection.

The wildcard must be leading. sub.*.example.com is not a valid source expression. Only *.example.com is recognized.

The default-src fallback that does not exist

Most CSP fetch directives fall back to default-src when not specified. frame-ancestors does not. It is a navigation directive, not a fetch directive, and the CSP specification explicitly excludes it from the fallback chain.

A policy of default-src 'self' does nothing to prevent clickjacking. If frame-ancestors is not set, the page can be framed by anyone regardless of how restrictive the rest of your CSP is. This is the most common CSP misconfiguration that leaves sites open to clickjacking.

Check your CSP. If you see a policy with default-src,script-src, and style-src but no frame-ancestors, your site has no clickjacking protection -- even if the policy looks strict.

The meta tag that does not work

frame-ancestors cannot be set via a <meta> tag. The CSP Level 3 specification explicitly lists it alongside sandbox and report-uri as directives that are not supported in the http-equiv attribute.

<!-- This has no effect. The browser ignores it. -->
<meta http-equiv="Content-Security-Policy" content="frame-ancestors 'none'">

The same restriction applies to X-Frame-Options. Neither clickjacking defense can be deployed from HTML. Both must be delivered as HTTP response headers from your server or CDN.

frame-ancestors vs frame-src

These two directives address opposite threat models.

frame-ancestorsframe-src
What it controlsWho can embed your pageWhat iframes your page can load
Threat modelClickjacking, UI redressingMalicious embedded content
Directive typeNavigation directiveFetch directive
default-src fallbackNoYes (via child-src)
Meta tag supportNoYes
CSP versionLevel 2Level 1

Source: MDN, July 2026. frame-src decides what your page can embed. frame-ancestors decides who can embed your page. They are independent and both should be set for a complete framing policy.

frame-ancestors vs X-Frame-Options

When both headers are present and the browser supports CSP, an enforced frame-ancestors directive takes precedence. A Content-Security-Policy-Report-Only header with frame-ancestors does not override X-Frame-Options -- only an enforced policy does.

Featureframe-ancestorsX-Frame-Options
Multiple originsYesNo (ALLOW-FROM is one origin, and obsolete)
Wildcard subdomainsYesNo
Checks full ancestor chainYesNo (only immediate parent)
Works in IENoYes (IE8+)
Meta tag supportNoNo
Report-only modeYes (via CSP-Report-Only)No
Browser coverageBaseline 2018 (all modern browsers)All browsers since IE8

Shipping both headers together is a reasonable baseline. The CSP directive covers modern browsers; XFO covers old browsers and provides defense-in-depth.

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

Browser support

Browserframe-ancestors
Chrome40+ (January 2015)
Firefox33–57 partial, 58+ full
Safari10+ (September 2016)
iOS Safari9.3+ (March 2016)
Edge15+ (April 2017)
Opera26+ (December 2014)
Samsung Internet4+ (April 2016)
Internet ExplorerNot supported (any version)

Source: CanIUse, July 2026. MDN lists Baseline Widely Available since January 2018. Internet Explorer is the only notable gap -- it never supported CSP Level 2. Use X-Frame-Options as a fallback if IE support matters.

How to set frame-ancestors

The directive must be delivered as part of the Content-Security-Policy HTTP response header. These one-liners get you started; each platform name links to a full configuration guide.

PlatformSnippet
NGINXadd_header Content-Security-Policy "frame-ancestors 'none'" always;
ApacheHeader always set Content-Security-Policy "frame-ancestors 'none'"
CloudflareTransform Rules → Set CSP header with frame-ancestors 'none'
WordPressheader("Content-Security-Policy: frame-ancestors 'none'");
Next.js + Vercelheaders() in next.config.mjs: frame-ancestors 'none'
Express.jsres.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
IIS<add name="Content-Security-Policy" value="frame-ancestors 'none'" />

Test your site now →