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.comThe 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.comA 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.
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-ancestors | frame-src | |
|---|---|---|
| What it controls | Who can embed your page | What iframes your page can load |
| Threat model | Clickjacking, UI redressing | Malicious embedded content |
| Directive type | Navigation directive | Fetch directive |
default-src fallback | No | Yes (via child-src) |
| Meta tag support | No | Yes |
| CSP version | Level 2 | Level 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.
| Feature | frame-ancestors | X-Frame-Options |
|---|---|---|
| Multiple origins | Yes | No (ALLOW-FROM is one origin, and obsolete) |
| Wildcard subdomains | Yes | No |
| Checks full ancestor chain | Yes | No (only immediate parent) |
| Works in IE | No | Yes (IE8+) |
| Meta tag support | No | No |
| Report-only mode | Yes (via CSP-Report-Only) | No |
| Browser coverage | Baseline 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: DENYBrowser support
| Browser | frame-ancestors |
|---|---|
| Chrome | 40+ (January 2015) |
| Firefox | 33–57 partial, 58+ full |
| Safari | 10+ (September 2016) |
| iOS Safari | 9.3+ (March 2016) |
| Edge | 15+ (April 2017) |
| Opera | 26+ (December 2014) |
| Samsung Internet | 4+ (April 2016) |
| Internet Explorer | Not 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.
| Platform | Snippet |
|---|---|
| NGINX | add_header Content-Security-Policy "frame-ancestors 'none'" always; |
| Apache | Header always set Content-Security-Policy "frame-ancestors 'none'" |
| Cloudflare | Transform Rules → Set CSP header with frame-ancestors 'none' |
| WordPress | header("Content-Security-Policy: frame-ancestors 'none'"); |
| Next.js + Vercel | headers() in next.config.mjs: frame-ancestors 'none' |
| Express.js | res.setHeader('Content-Security-Policy', "frame-ancestors 'none'"); |
| IIS | <add name="Content-Security-Policy" value="frame-ancestors 'none'" /> |
Read next
- X-Frame-Options Reference -- DENY, SAMEORIGIN, and ALLOW-FROM
- NGINX clickjacking protection
- Apache clickjacking protection
- Cloudflare 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: Missing Anti-Clickjacking Header? What the scanner warning means