X-Frame-Options and "refused to connect"

You put a page in an <iframe> and get a blank box with refused to connect. Nothing is broken: the other site told your browser not to frame it. Here is what the header does, how to set it for your own site, and what you can honestly do when it isn't your site.

What the header says

X-Frame-Options: DENY        # no site may frame this page
X-Frame-Options: SAMEORIGIN  # only pages from the same origin may

It exists to stop clickjacking: someone frames a real, logged-in page, covers it with their own buttons, and gets a click that goes to the real site. Browsers enforce it before the page renders, which is why you see the error and no content.

The modern replacement: frame-ancestors

Content-Security-Policy does the same job with more control, and where both are present, frame-ancestors wins in modern browsers:

Content-Security-Policy: frame-ancestors 'self' https://partner.example.com;

If your iframe is still refused after you removed X-Frame-Options, check for this header too.

Your own site: allow the framing you want

Send one header, from the server that serves the page being framed.

# nginx
add_header Content-Security-Policy "frame-ancestors 'self' https://partner.example.com" always;

# Apache
Header always set Content-Security-Policy "frame-ancestors 'self' https://partner.example.com"

# Express
app.use((req, res, next) => {
  res.setHeader("Content-Security-Policy", "frame-ancestors 'self' https://partner.example.com");
  next();
});

Name the origins you actually want. frame-ancestors * allows everyone, which brings back the clickjacking risk the header exists to stop.

Not your site: what's honest

Removing the header while you test

HeaderForge has a one-click preset that removes X-Frame-Options (and one for Content-Security-Policy) for the sites you choose, in your browser only, with a switch to turn it off again. Free, no analytics, no account.

Get HeaderForge for Chrome

Checklist when an iframe won't load

  1. Open DevTools → Console: the message names the header that blocked it.
  2. Check the framed page's response headers in the Network tab for X-Frame-Options and Content-Security-Policy.
  3. If it's your page, set frame-ancestors and drop X-Frame-Options.
  4. If it isn't, ask the owner, or use their embed. CORS errors are a different problem with a similar feel.

More on this topic: “Refused to load … violates the following Content Security Policy directive”: fixes · Mixed content error: “loaded over HTTPS, but requested an insecure resource” · How to change your user agent in Chrome (and what it really changes) · ModHeader alternative