Skip to content
ClickJack Test

← Guides

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, has worked in every major browser since January 2018, and is Baseline Widely Available. It replaces X-Frame-Options, covering everything that header can do and several things it cannot.

X-Frame-Options has two working values (DENY and SAMEORIGIN). frame-ancestors takes a source list, so it can allow several specific origins and wildcard subdomains. It 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, and 'none' must be the only value (combined with other sources it is ignored). This is the equivalent of 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. Separate multiple sources with spaces. This is what X-Frame-Options: ALLOW-FROM tried to do, but ALLOW-FROM only took one origin, never worked in Chrome or Safari, and is ignored by current browsers.

The allowed values are narrower than in other directives. frame-ancestors accepts only 'self', 'none', scheme sources (https:), and host sources (https://example.com, *.example.com). Keywords such as 'unsafe-inline', nonces, and hashes are not valid here.

Ancestors are matched by origin: scheme, host, and port. A source without a port only matches the scheme's default port, so add :8443 or :* for others. Leave paths off; you are naming sites, not pages.

If a response carries more than one policy (several Content-Security-Policy headers, or policies joined with commas), every one is enforced, so the most restrictive frame-ancestors wins. Inside a single policy, only the first frame-ancestors directive counts; duplicates are ignored. A directive with an empty value blocks everything, like 'none'.

The wildcard gotcha

Wildcards are allowed, but they do not always match what you expect.

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

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

A bare asterisk allows everything. frame-ancestors * is valid CSP and lets any website frame the page. So do https://* and the scheme source https:, which match every HTTPS site. Scanners that flag "CSP: Wildcard Directive" on frame-ancestors are right: these values give no clickjacking protection.

The wildcard must be the leftmost label. sub.*.example.com is not a valid source expression; *.example.com is.

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 states that it never falls back to default-src.

Even default-src 'none' does nothing to prevent clickjacking. Without frame-ancestors, anyone can frame the page, however strict the rest of your CSP is. This is a common way for a site with a CSP to end up exposed.

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 says browsers must ignore it in a policy delivered by <meta http-equiv>, and lists it with sandbox and report-uri as directives meta tags do not support.

<!-- 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 works from HTML. Both must be sent 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, then default-src)
Meta tag supportNoYes
CSP versionLevel 2Level 1

Source: MDN and the CSP Level 3 specification, October 2026. frame-src decides what your page can embed. frame-ancestors decides who can embed your page. They are independent: setting one does nothing for the other, and only frame-ancestors stops clickjacking.

frame-ancestors vs X-Frame-Options

When an enforced CSP contains frame-ancestors, browsers ignore X-Frame-Options entirely, even if it is stricter. So frame-ancestors * plus X-Frame-Options: DENY leaves the page frameable by anyone. A Content-Security-Policy-Report-Only header does not override X-Frame-Options, and does not block anything itself; it only sends violation reports.

Featureframe-ancestorsX-Frame-Options
Multiple originsYesNo (ALLOW-FROM took one origin and is obsolete)
Wildcard subdomainsYesNo
Checks full ancestor chainYesSAMEORIGIN does in current browsers (Chrome 61+, Firefox 59+); older ones varied
Works in IENoYes (IE8+)
Meta tag supportNoNo
Report-only modeYes (via CSP-Report-Only)No
Browser coverageAll modern browsers (since January 2018); not IEAll browsers since IE8

Sending both headers is a reasonable baseline. Modern browsers use frame-ancestors and ignore XFO; XFO only matters in browsers without CSP Level 2, such as IE. Because each browser obeys only one of them, keep the two saying the same thing.

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

Browser support

Browserframe-ancestors
Chrome40+ (January 2015)
Firefox33+ (October 2014); Report-Only from 58
Safari10+ (September 2016)
iOS Safari9.3+ (March 2016)
Edge15+ (April 2017)
Opera26+ (December 2014)
Samsung Internet4+ (April 2016)
Internet ExplorerNot supported (any version)

Source: MDN browser-compat-data (also used by Can I Use), October 2026. Firefox 33 to 57 ignored frame-ancestors in Report-Only policies. Internet Explorer is the only notable gap: it never supported CSP Level 2. Send X-Frame-Options as well if IE support matters.

How to set frame-ancestors

Send it in the Content-Security-Policy HTTP response header. These one-liners get you started; linked platform names go to a full configuration guide. If you already send a CSP, add frame-ancestors to that policy rather than sending a second header.

PlatformSnippet
NGINXadd_header Content-Security-Policy "frame-ancestors 'none'" always;
ApacheHeader always set Content-Security-Policy "frame-ancestors 'none'"
CloudflareResponse Header Transform Rule → Set static: Content-Security-Policy = frame-ancestors 'none'
WordPressadd_action('send_headers', fn() => header("Content-Security-Policy: frame-ancestors 'none'"));
Next.js + Vercel{ key: 'Content-Security-Policy', value: "frame-ancestors 'none'" } in headers() in next.config.js
Express.jsres.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
IIS<add name="Content-Security-Policy" value="frame-ancestors 'none'" />

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.