Error guide / Refused to display in a frame (X-Frame-Options)
Refused to display in a frame (X-Frame-Options)
The browser blocked a page from rendering inside your iframe. The site you are embedding answered the request with a header saying it does not want to be framed, so the frame stays empty and the complaint goes to the console instead of the page. You normally see this while building an embed, a widget, a preview pane or an admin panel that pulls in another site; a regular visitor just sees a blank box. Nothing is broken on your end: the request reached the other server, was answered, and the browser refused to display the result.
Check your site
What causes it
Most often the embedded site simply sets X-Frame-Options: deny (nobody may frame it) or sameorigin (only pages on its own origin may), which is the default for banks, Google, most SaaS dashboards and many WordPress or nginx security configs. Next most common is Content-Security-Policy with a frame-ancestors directive, which does the same job and, in every current browser, overrides X-Frame-Options when both are present, so a site can look permissive in one header and still block you through the other. A frequent self-inflicted case is your own server, security plugin or reverse proxy adding sameorigin site-wide, which then blocks your own embed the moment the frame is served from a different subdomain, port or scheme. Also common is inherited X-Frame-Options: allow-from https://example.com, which no current browser supports, so it either does nothing or is treated as invalid and framing stays blocked.
How to fix it
- Run the security headers check on this page against the exact URL you are embedding, not against your own page, and read two things in the result: the X-Frame-Options value and whether frame-ancestors appears in the list of Content-Security-Policy directives. If frame-ancestors is present, that is the rule the browser actually enforces and X-Frame-Options is ignored; if it is absent, X-Frame-Options decides, and deny blocks every site while sameorigin blocks everyone except the framed site itself.
- If the check shows frame-ancestors, open your browser devtools, Network tab, click the framed request and read the full Content-Security-Policy value under Response Headers to see the allowed list. 'none' blocks everyone, 'self' allows only that site, and an explicit list allows exactly those origins with scheme and port matched literally, so https://app.example.com does not cover http://, a different port or a different subdomain.
- If you control the framed site, relax the policy deliberately rather than removing it: send Content-Security-Policy: frame-ancestors 'self' https://your-app.example.com and list every origin allowed to embed you, then align or drop X-Frame-Options so the two do not contradict each other. Deleting both headers instead re-opens clickjacking on every page of that site.
- Remove any X-Frame-Options: allow-from ... you inherited from an old guide. It only ever worked in Internet Explorer and old Firefox builds, current browsers ignore it or treat the header as invalid, and frame-ancestors is now the only way to allowlist specific origins.
- Deploy, purge the CDN or page cache, hard-reload, then run the check again on the same URL: header changes are frequently cached, and a second X-Frame-Options added by a proxy, security plugin or nginx add_header can still be sitting in front of your new policy. The result should now show your intended value, and only then is it worth retesting the actual iframe.
- If the site is not yours, accept that there is no client-side workaround: no sandbox attribute, script or header-stripping proxy is a legitimate fix, because the header is the owner's decision not to be embedded, and proxy tricks break their logins and usually their terms of service. Use their official embed code, public API or oEmbed endpoint, show a link with a preview card, or ask the owner to add your origin to their frame-ancestors list.