Iframe Checker

Paste a URL to find out whether it will load inside an iframe on another site. The tool fetches the page from our edge, follows redirects the way a frame does, and reads the two headers that control framing: X-Frame-Options and the frame-ancestors directive of Content-Security-Policy. You get a verdict for any other site, for the page's own origin and for your domain if you enter it, plus a live preview and ready-to-paste embed code. Your browser cannot run this check by itself, because it never lets one site's JavaScript read another site's response headers.

Enter a URL to see whether it can be embedded in an iframe

How the verdict is decided

The checks follow the HTML Standard and CSP Level 3, which current Chrome, Firefox and Safari implement, and run in the order a browser runs them. Mixed content comes first, because an https page cannot frame an http:// page at all. Next, if the response has a Content-Security-Policy with frame-ancestors, that list alone decides and X-Frame-Options is ignored, even when it is stricter. Without frame-ancestors, X-Frame-Options: DENY blocks every parent and SAMEORIGIN allows only pages on the exact same scheme, host and port. With neither header, any site can frame the page.

What each header value does

If a page sends both headers, only its frame-ancestors row applies.

Header on the framed pageWho can embed it
No X-Frame-Options and no frame-ancestorsAny site
X-Frame-Options: DENYNobody
X-Frame-Options: SAMEORIGINOnly pages on the same scheme, host and port
X-Frame-Options: ALLOW-FROM https://a.exampleAny site, because browsers ignore the value
X-Frame-Options: DENY, SAMEORIGINNobody, because conflicting values count as DENY
frame-ancestors 'none'Nobody
frame-ancestors 'self'Only its own origin
frame-ancestors 'self' https://partner.exampleIts own origin and https://partner.example
frame-ancestors https://*.example.comSubdomains of example.com, but not example.com itself
frame-ancestors *Any http or https site
frame-ancestors in Content-Security-Policy-Report-OnlyAny site, because report-only never blocks
frame-ancestors in a <meta> tagIgnored, so the HTTP headers decide

Sources in frame-ancestors follow the CSP Level 3 matching rules: https://*.example.com covers subdomains but not example.com itself, a source without a port only matches the default port, an http source also matches https, and a source with a path never matches, because ancestors are compared as bare origins. Both headers apply to every ancestor, so in nested frames each level has to be allowed.

Allow or block embedding on your own site

Send one header from the server, not a meta tag, because browsers ignore frame-ancestors and X-Frame-Options in HTML. List every origin that may embed the page; keep 'self' if your own pages frame it too.

# Only your own pages and one partner may frame it
Content-Security-Policy: frame-ancestors 'self' https://partner.example

# Nobody may frame it (clickjacking protection)
Content-Security-Policy: frame-ancestors 'none'

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

# Apache (.htaccess, needs mod_headers)
Header always set Content-Security-Policy "frame-ancestors 'self' https://partner.example"

If the page already sends a Content-Security-Policy, add frame-ancestors to that header. A second policy is enforced on top of the first, so any directive that appears in both has to pass twice. Once frame-ancestors is in place, X-Frame-Options only matters to browsers too old to know CSP Level 2.

How to use the Iframe Checker (Can This Site Be Embedded?) tool

  1. Paste the URL of the page you want to embed. https:// is added if you leave the scheme out.
  2. Optionally type the site you want to embed it on, such as https://yoursite.com, in the Your site box. Without it you still get the answer for any other site and for the page's own origin.
  3. Click Check Iframe. The tool fetches the URL from our edge, follows redirects the way a frame does, and reads X-Frame-Options and Content-Security-Policy on the final response.
  4. Read the verdict and the Who can embed it rows, each with the rule that decided it. The Notes list flags problems such as ALLOW-FROM, a meta tag that browsers ignore, an http:// page or a frame-busting script.
  5. Check the live preview at full, desktop, tablet or phone width, then click Copy to take the embed code, which points at the final URL. Dummy Data runs a worked example.

How to check if a page can be framed in code

curl

# -L follows redirects and prints the headers of every hop; the last block is the one that counts
curl -sIL https://stripe.com | grep -iE "^(HTTP/|x-frame-options:|content-security-policy:)"

# Only the frame-ancestors directive
curl -sIL https://stripe.com | grep -i "^content-security-policy:" | tr ";" "\n" | grep -i frame-ancestors

-I sends HEAD, and a few servers answer it with different headers. curl -sL -o /dev/null -D - <url> prints the headers of a real GET instead.

JavaScript (Node.js)

// Simplified: handles 'none', 'self', * and exact or *.wildcard hosts.
// The tool also applies the CSP3 rules for schemes, ports, paths and multiple policies.
async function canFrame(url, parentOrigin) {
    const res = await fetch(url); // follows redirects
    const self = new URL(res.url).origin;
    const csp = res.headers.get("content-security-policy") ?? "";
    const fa = csp.split(";").map((d) => d.trim().split(/\s+/))
        .find(([name]) => name.toLowerCase() === "frame-ancestors");

    if (fa) {
        const parentHost = new URL(parentOrigin).hostname;
        return fa.slice(1).some((src) => {
            src = src.toLowerCase();
            if (src === "*") return true;
            if (src === "'self'") return parentOrigin === self;
            const host = src.replace(/^[a-z]+:\/\//, "").replace(/[:/].*$/, "");
            return host.startsWith("*.") ? parentHost.endsWith(host.slice(1)) : parentHost === host;
        });
    }
    const xfo = (res.headers.get("x-frame-options") ?? "").trim().toLowerCase();
    if (xfo === "deny") return false;
    if (xfo === "sameorigin") return parentOrigin === self;
    return true; // no header, or a value browsers ignore
}

console.log(await canFrame("https://stripe.com", "https://app.contentful.com")); // true
console.log(await canFrame("https://github.com", "https://example.com")); // false

Run it on a server. In a browser, fetch() to another origin either fails on CORS or hides these headers, which is why the tool runs on the edge.

Python (requests)

import requests

def frame_headers(url):
    r = requests.get(url, timeout=10)  # follows redirects
    csp = r.headers.get("Content-Security-Policy", "")
    fa = next((d.strip() for d in csp.split(";") if d.strip().lower().startswith("frame-ancestors")), None)
    return {
        "final_url": r.url,
        "x_frame_options": r.headers.get("X-Frame-Options"),
        "frame_ancestors": fa,  # when set, browsers ignore X-Frame-Options
    }

print(frame_headers("https://github.com"))
# {'final_url': 'https://github.com/', 'x_frame_options': 'deny', 'frame_ancestors': "frame-ancestors 'none'"}

Common errors and how to fix them

Refused to display '...' in a frame because it set 'X-Frame-Options' to 'sameorigin'
The page allows only its own origin. If it is your site, replace the header with Content-Security-Policy: frame-ancestors 'self' https://yoursite.example. If it is someone else's, look for an official embed URL; nothing on the embedding page can override the header.
Refused to frame '...' because an ancestor violates the following Content Security Policy directive: "frame-ancestors ..."
Your origin is not on the list. Add it exactly as the browser sees it, scheme included: https://www.example.com and https://example.com are different origins, and https://*.example.com does not cover the bare domain.
Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure frame 'http://...'
Change the iframe src to https://. A page that has no https version cannot be embedded on an https site at all, whatever its headers say.
The Content Security Policy directive 'frame-ancestors' is ignored when delivered via a <meta> element
Move the directive into the HTTP response header, through the server config, a CDN rule or framework middleware. A meta CSP supports most directives but not frame-ancestors, sandbox or report-uri.
Invalid 'X-Frame-Options' header encountered when loading '...': 'ALLOW-FROM ...' is not a recognized directive
Replace it with Content-Security-Policy: frame-ancestors https://partner.example. Until then the page has no framing protection at all, because the ignored header leaves every site free to frame it.

Good to know

Both headers are checked against every ancestor up to the top-level page. If site A frames site B, and B frames the page, A and B must both be allowed. The verdicts here assume one level, which is how almost every embed works.

The check runs from Cloudflare's network with no cookies. A page that sends different headers to logged-in users, or a bot challenge to unknown clients, can look different to a real visitor; the live preview shows what your own browser does with the real response.

Specifications and references

This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.

Frequently Asked Questions

Why does a website refuse to connect in an iframe?

The site sends X-Frame-Options: DENY or SAMEORIGIN, or a Content-Security-Policy frame-ancestors list that does not include your domain. The browser fetches the page, reads the header and shows an error such as "refused to connect" instead of the content. No attribute on your iframe can override it; only the site's owner can change the header.

What is the difference between X-Frame-Options and frame-ancestors?

Both tell the browser which pages may frame a URL. X-Frame-Options is the older header and has two working values, DENY and SAMEORIGIN. CSP frame-ancestors replaces it and takes a list: 'none', 'self', exact origins, schemes and wildcards such as https://*.example.com. When a response has both, browsers follow frame-ancestors and ignore X-Frame-Options.

Can I use it to test a page for clickjacking?

Yes, for the header side. Clickjacking only works if the attacker can load your page in a frame. If a page with logged-in actions, such as account settings or a delete button, comes back as embeddable by any site, it has no protection. Fix it with Content-Security-Policy: frame-ancestors 'none', or 'self' if your own pages frame it. The check runs without cookies, so if your server sends different headers to logged-in users, also test with curl and your session cookie.

Does X-Frame-Options ALLOW-FROM still work?

No. Chrome and Safari never supported it, and Firefox removed it. A browser that does not recognize the value ignores the whole header, so a page that sends ALLOW-FROM can be framed by any site. Use Content-Security-Policy: frame-ancestors https://partner.example instead.

The headers allow framing, so why is my iframe blank?

Either the src is http:// on an https page, which the browser blocks as mixed content, or the page runs frame-busting JavaScript that hides its content or tries to replace the parent page when it detects a frame. Content behind a login is the other common case: cookies without SameSite=None; Secure never reach a cross-site frame, Safari blocks third-party cookies and Firefox partitions them.

Why does the preview fail when the result says my site can embed it?

The preview is framed by webdevutility.com, not by your site, so it shows what any other website gets. A page that lists specific domains in frame-ancestors blocks the preview but still works on those domains. The Your site row is the exact answer for your origin.

How can I embed a site that blocks framing?

Look for its official embed URL. YouTube watch pages send X-Frame-Options: SAMEORIGIN, but the /embed/ URLs allow framing, and Google Maps and most social networks have similar embed links. Otherwise link to the page or ask the owner to add your origin to frame-ancestors. Proxying the page through your own server to strip the headers breaks logins and usually breaks the site's terms too.

Related

Follow every hop of the src with the redirect checker, confirm the page is up with the website down checker, or look up what a 403 or 404 in the result means in the HTTP status code list. For a widget made of an element and a script tag, which runs in your page rather than in a frame, use the embed code tester.

More URL Tools

Browse all url tools

Copied to clipboard!