Refused to display in a frame because an ancestor violates frame-ancestors / X-Frame-Options
An embed is blank, the console says the page “refused to display in a frame”, and your payment widget, docs iframe or admin preview is empty. The page is blocking being framed — usually on purpose.
What the error means
Browsers show one of these (wording varies slightly):
Refused to display 'https://app.example/…' in a frame because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self'"Refused to display '…' in a frame because it set 'X-Frame-Options' to 'deny'(or'sameorigin')
The check runs on the document inside the iframe. Your outer page is fine; the inner response's headers said “don't let this parent embed me.” That is clickjacking defence: without it, a hostile site could overlay invisible UI on top of your logged-in page and steal clicks.
DENY vs SAMEORIGIN vs frame-ancestors
| Header | Effect | Notes |
|---|---|---|
X-Frame-Options: DENY | Nobody may frame the page, not even the same site | Oldest, widely supported |
X-Frame-Options: SAMEORIGIN | Only the exact same origin (scheme + host + port) | Subdomains do not count |
X-Frame-Options: ALLOW-FROM https://partner.example | Legacy IE only | Chromium, Firefox and Safari ignore it |
Content-Security-Policy: frame-ancestors 'none' | Same idea as DENY | Modern replacement; not allowed in a <meta> tag |
frame-ancestors 'self' | Same idea as SAMEORIGIN | Preferred over XFO |
frame-ancestors 'self' https://trusted.example | You plus listed parents | How to allow a real embed partner |
When both XFO and frame-ancestors are present, modern browsers obey frame-ancestors and ignore XFO for framing. default-src never covers frame-ancestors — you must set it yourself. Paste your headers into the X-Frame-Options & frame-ancestors explainer to see the effective rule and try attacker origins. From a script or CI job, the free Anvil framing / clickjacking API returns the same grade and check as JSON.
Allow a trusted parent
# Prefer CSP (overrides XFO when both are sent)
Content-Security-Policy: frame-ancestors 'self' https://trusted.example https://*.partner.example;
# nginx
add_header Content-Security-Policy "frame-ancestors 'self' https://trusted.example" always;
# Express
res.setHeader("Content-Security-Policy", "frame-ancestors 'self' https://trusted.example");
List every parent that must embed you, including preview domains and local dev (http://localhost:3000) if needed. Wildcards cover one DNS label (*.partner.example matches a.partner.example, not partner.example itself). After changing headers, hard-refresh; CDNs and service workers can cache the old policy.
How to harden (when you are the framed site)
- Sensitive pages (login, checkout, account settings):
frame-ancestors 'none'orX-Frame-Options: DENY. - Same-site embeds only:
frame-ancestors 'self'. - Never rely on
ALLOW-FROMalone. - Send framing policy as an HTTP header, not a meta tag.
- Combine with cookie
SameSite(see the Set-Cookie explainer) — framing defence and cookie rules solve different attacks.
Related
Refused to execute inline script (CSP) · CSP header explainer · CSRF token missing · CORS errors · CSP Report-Only vs Enforce · Wirebook bots lesson · NotAllowedError getUserMedia (iframe allow)
Questions
What does 'an ancestor violates frame-ancestors' mean?
The page inside the iframe sent a CSP frame-ancestors list that does not include the parent page's origin, so the browser refuses to paint it.
How do I allow my site to be embedded by a partner?
On the framed site, send Content-Security-Policy: frame-ancestors 'self' https://partner.example (plus any other parents). Prefer that over X-Frame-Options ALLOW-FROM.
Why does SAMEORIGIN block my subdomain?
SAMEORIGIN and 'self' mean the exact origin. www.example.com and app.example.com are different. List each host in frame-ancestors, or use a matching wildcard carefully.
Does removing X-Frame-Options fix a blank iframe?
It might, but only do that if the page is meant to be public to embed. For logins and payments, keep DENY / 'none' and embed a dedicated public widget instead.
Can a sandboxed iframe bypass the block?
No. Sandbox does not override X-Frame-Options or frame-ancestors.
Published .