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.comThe 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.comA 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.
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-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, then default-src) |
| Meta tag support | No | Yes |
| CSP version | Level 2 | Level 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.
| Feature | frame-ancestors | X-Frame-Options |
|---|---|---|
| Multiple origins | Yes | No (ALLOW-FROM took one origin and is obsolete) |
| Wildcard subdomains | Yes | No |
| Checks full ancestor chain | Yes | SAMEORIGIN does in current browsers (Chrome 61+, Firefox 59+); older ones varied |
| Works in IE | No | Yes (IE8+) |
| Meta tag support | No | No |
| Report-only mode | Yes (via CSP-Report-Only) | No |
| Browser coverage | All modern browsers (since January 2018); not IE | All 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: DENYBrowser support
| Browser | frame-ancestors |
|---|---|
| Chrome | 40+ (January 2015) |
| Firefox | 33+ (October 2014); Report-Only from 58 |
| 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: 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.
| Platform | Snippet |
|---|---|
| NGINX | add_header Content-Security-Policy "frame-ancestors 'none'" always; |
| Apache | Header always set Content-Security-Policy "frame-ancestors 'none'" |
| Cloudflare | Response Header Transform Rule → Set static: Content-Security-Policy = frame-ancestors 'none' |
| WordPress | add_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.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