Why Won't This Site Load in an Iframe? (X-Frame-Options Explained)
2026-09-01
You drop a URL into an iframe, refresh the page, and get a blank rectangle where content should be. No error in your code, no typo in the URL — the page loads fine in its own tab. This is almost always the target site refusing to be framed, and it's intentional.
The header doing the blocking
Browsers respect two mechanisms that let a site opt out of being embedded:
X-Frame-Options — an HTTP response header with three possible values:
DENY— can't be framed by anyone, including the site's own pagesSAMEORIGIN— can only be framed by pages on the same domainALLOW-FROM url— deprecated, ignored by modern browsers
Content-Security-Policy: frame-ancestors — the newer, more flexible replacement. Instead of an all-or-nothing rule, frame-ancestors can list exactly which domains are allowed to embed the page: frame-ancestors 'self' https://trusted-partner.com permits the site's own pages plus one named partner, and blocks everyone else.
When both headers are present, browsers give frame-ancestors priority. A site can also combine either header with JavaScript "frame-busting" code that detects it's inside a frame and forces itself out — common on banking and social platforms as a second layer of defense.
Why sites do this on purpose
The main reason is clickjacking: a malicious site stacks an invisible iframe of your logged-in banking or email session on top of what looks like an innocent button, so your real click lands on the hidden iframe instead. Blocking framing entirely closes that attack off. It's also why you'll never get Google Search, most banks, or logged-in social platforms to load in an iframe, no matter what you try — there's no workaround for a page you don't control, because the browser is enforcing it, not the page's own JavaScript.
Checking before you build
Finding this out after you've already built an embed widget or client demo around a site that turns out to block framing is a wasted afternoon. The Iframe Tester loads a URL inside a real iframe and reports exactly what's blocking it — X-Frame-Options, CSP frame-ancestors, or neither — before you commit to building around it.
If you want to check it yourself without any tool: open your browser's dev tools, go to the Network tab, load the target page directly, click the request, and look at the Response Headers for x-frame-options or content-security-policy. If either is present with a restrictive value, that's your answer.
What to do if it's your own site
If you're the one setting these headers and something you expect to be embeddable isn't loading — a widget, a map, an internal dashboard — check your server or CDN config for a global X-Frame-Options: DENY or SAMEORIGIN that might be more restrictive than you intended. A lot of security header presets (Helmet.js defaults, some CDN "security mode" toggles) set this to DENY automatically, which blocks your own legitimate embeds along with everyone else's.
FAQ
Can I bypass X-Frame-Options if I don't own the site? No, and nothing legitimately can — it's enforced by the browser, not by the page itself. If you need a site's content embedded and it blocks framing, the options are asking the owner to adjust their headers, or building an integration through their API instead of an iframe.
Why does a site load fine in a normal tab but not in my iframe? Because framing restrictions only apply to framed contexts. The page works completely normally as a direct visit; the header specifically targets the case where another page is trying to wrap it in a frame.
Does this affect <embed> and <object> tags too?
Yes — X-Frame-Options and frame-ancestors apply to any framing context, not just <iframe> specifically.