CSP frame-ancestors
frame-ancestors is a Content-Security-Policy directive that lists the sites allowed to put your page in a frame. It works only as an HTTP header, not in a meta tag. When it is enforced, browsers ignore X-Frame-Options.
5 min read · Updated
Syntax
Content-Security-Policy: frame-ancestors <source> <source> ...
| Value | Who can frame the page |
|---|---|
'none' | Nobody. Same as X-Frame-Options: DENY. |
'self' | Pages with the same origin. |
https://partner.example | Pages on that exact origin. |
https://*.partner.example | Pages on any subdomain of partner.example. The bare domain partner.example does not match. |
https: | Any site on HTTPS. |
* | Any site. |
Put the single quotes around 'none' and 'self'. Without quotes, the browser reads self as a host name.
Common policies
Allow only your own site:
Content-Security-Policy: frame-ancestors 'self'
Allow your site and one partner:
Content-Security-Policy: frame-ancestors 'self' https://partner.example
Allow a partner and all its subdomains:
Content-Security-Policy: frame-ancestors https://partner.example https://*.partner.example
Block all frames:
Content-Security-Policy: frame-ancestors 'none'
If you already send a CSP, add frame-ancestors to it. Separate directives with a semicolon:
Content-Security-Policy: default-src 'self'; img-src 'self' data:; frame-ancestors 'self' https://partner.example
Rules that cause surprises
Every ancestor must match
The browser checks each frame above the page, up to the top window. If https://partner.example frames a page that frames yours, both origins must be in your list.
It does not fall back to default-src
default-src 'none' does not block framing. If a policy has no frame-ancestors, any site can frame the page, unless X-Frame-Options blocks it.
A meta tag does not work
<!-- frame-ancestors is ignored here -->
<meta http-equiv="Content-Security-Policy" content="frame-ancestors 'self'" />
Browsers ignore frame-ancestors in a <meta> tag. Send it as an HTTP response header.
Report-only does not block
Content-Security-Policy-Report-Only: frame-ancestors 'self'
This header only reports. The page still loads in any frame. It also does not switch off X-Frame-Options. Use it to test a new list before you enforce it.
Two policies: both must allow the frame
The app and the proxy can each send a Content-Security-Policy header. The browser enforces every policy. A frame loads only if all of them allow it. So frame-ancestors 'self' from the app still blocks the partner when the proxy adds a second policy that lists https://partner.example.
Edit the one policy that already exists. Do not add a second one.
Use origins, not paths
List the scheme and host, plus the port if it is not the default. Do not add a path like https://partner.example/app. The browser compares the origin of each ancestor, so a path can make the match fail.
How it works with X-Frame-Options
The HTML standard says browsers skip X-Frame-Options when an enforced CSP has frame-ancestors. So this response can be framed by the partner:
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self' https://partner.example
Remove X-Frame-Options anyway when you allow other sites. It confuses people who read the headers, and some scanners still flag it. See X-Frame-Options.
The error you see when it blocks
Chrome shows "refused to connect" in the frame and prints this in the console:
Framing 'https://your-site.com/' violates the following Content Security Policy directive: "frame-ancestors 'self'". The request has been blocked.
Older Chrome versions print "Refused to frame ... because an ancestor violates the following Content Security Policy directive". Each browser words it in its own way. These pages show the exact text, the cause, and the fix:
- Refused to frame: an ancestor violates CSP frame-ancestors (Chrome and Edge)
- Refused to load: not in the frame-ancestors directive (Safari)
- Firefox Can't Open This Page (Firefox)
To match any other frame error to its header, see Find the header that blocks your iframe.
Try your policy
After you deploy, paste the page URL into the iframe tester. It checks if the page can load in a frame, explains the result, and shows the fix.
To check many pages at once, use the bulk check. It shows X-Frame-Options and frame-ancestors for up to 100 URLs.