Anvil › Docs › /v1/frame/check
Clickjacking header API: X-Frame-Options and frame-ancestors checker
Clickjacking works when any site can put yours in an invisible iframe. The two headers that stop that are X-Frame-Options (DENY / SAMEORIGIN) and CSP frame-ancestors. Paste what your server sends and this endpoint grades the defence and optionally answers "would https://evil.example be allowed to frame this page?".
/v1/frame/check?header=X-Frame-Options%3A%20SAMEORIGIN&page=https%3A//app.example.com/&parent_kind=evil returns JSON. No key, CORS on, 60 requests a minute./v1/frame/check
Clickjacking headers: who may embed this page in a frame?. Change the values and press Send request to call the live API.
Parameters
| Name | Type | What it does | Default |
|---|---|---|---|
header | string | X-Frame-Options and/or a CSP with frame-ancestors, a header dump, or a <meta> tag. Up to 8 KB. | — |
page | string | The page being framed (scheme+host+port). Needed for SAMEORIGIN / 'self' and for parent checks. | — |
parent | string | Optional parent / ancestor URL to simulate (omit with parent_kind=null for an opaque origin) | — |
parent_kind | string | Optional shortcut instead of parent: same, example, evil, sub, null, sandbox, url | — |
Example
Request:
GET https://api.oddlathe.com/v1/frame/check?header=X-Frame-Options%3A%20SAMEORIGIN&page=https%3A//app.example.com/&parent_kind=evil
Response (generated by the real code when these docs were last built):
{
"grade": "protected",
"headers": [
{
"name": "X-Frame-Options",
"value": "SAMEORIGIN",
"mode": "SAMEORIGIN",
"uri": null,
"meaning": "Only a page with the exact same origin (scheme + host + port) may embed it. Subdomains do not count."
},
{
"name": "frame-ancestors",
"value": null,
"meaning": "Not present"
}
],
"findings": [],
"goods": [
"X-Frame-Options: SAMEORIGIN is set (same origin only).",
"Note: SAMEORIGIN means exact origin, not *.example.com. A parent on a subdomain is blocked.",
"Checks use page origin https://app.example.com."
],
"page": "https://app.example.com",
"check": {
"parent": "https://evil.example",
"page": "https://app.example.com",
"allowed": false,
"via": "X-Frame-Options",
"why": "X-Frame-Options: SAMEORIGIN — parent https://evil.example is not the same origin as https://app.example.com. Subdomains do not count.",
"console": "Refused to display 'https://app.example.com/' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.",
"notes": []
}
}
Code examples
curl -G 'https://api.oddlathe.com/v1/frame/check' \
--data-urlencode 'header=X-Frame-Options: SAMEORIGIN' \
--data-urlencode 'page=https://app.example.com/' \
--data-urlencode 'parent_kind=evil'const params = new URLSearchParams({
header: "X-Frame-Options: SAMEORIGIN",
page: "https://app.example.com/",
parent_kind: "evil"
});
const res = await fetch(`https://api.oddlathe.com/v1/frame/check?${params}`);
if (!res.ok) throw new Error((await res.json()).error.message);
const data = await res.json();
console.log(data);import json, urllib.parse, urllib.request
params = urllib.parse.urlencode({"header": "X-Frame-Options: SAMEORIGIN", "page": "https://app.example.com/", "parent_kind": "evil"})
url = "https://api.oddlathe.com/v1/frame/check?" + params
# Send a User-Agent: the edge network refuses Python's default one.
req = urllib.request.Request(url, headers={"User-Agent": "my-script/1.0"})
with urllib.request.urlopen(req, timeout=10) as r:
data = json.load(r)
print(data)When it's useful
- A CI check that login and checkout pages still send DENY, SAMEORIGIN or frame-ancestors 'none'/'self'
- Explaining a framing error ("ancestor violates frame-ancestors" / "X-Frame-Options") in a support answer
- Auditing a header dump from curl -I or DevTools before a release
- Teaching the difference between SAMEORIGIN and frame-ancestors 'self', and why ALLOW-FROM is ignored
Good to know
- Send a bare value (
DENY), anX-Frame-Options:line, a CSP that includesframe-ancestors, both together, a whole response-header dump, or a<meta http-equiv="Content-Security-Policy">tag (frame-ancestors in meta is reported as ignored). gradeisprotected,okayorneeds_work. Each item infindingshas acode, aseverity(error, warn, info) and amessageyou can show as it is.goodslists what the headers already do well.- Finding codes include
no_defence,allow_from_only,allow_from_deprecated,invalid_xfo,meta_ignored,broad_ancestors,csp_overrides_xfoandreport_only_fa. - Optional check: pass
pageplus eitherparent(any http(s) URL) orparent_kind(same,example,evil,sub,null,sandbox). The response includesallowed,via(which header decided), a plain-wordswhy, and the console wording browsers show when blocked. Modern rule: when both headers are present,frame-ancestorswins. - It never fetches anything. For a clickable simulator in the browser, use Tooltern's X-Frame-Options & frame-ancestors explainer. For a full CSP (scripts, hashes, connect-src), use /v1/csp/parse.
Reads X-Frame-Options and CSP frame-ancestors the way current browsers do: every value explained in plain words, a grade (protected / okay / needs_work), findings (missing defence, ALLOW-FROM ignored, meta frame-ancestors ignored, broad *, CSP overrides XFO), and an optional check that answers whether a given parent may embed the page. Modern rule: when both headers are present, frame-ancestors wins. Pure text analysis: nothing is fetched or stored.
Errors
Errors are JSON like {"error": {"code": "…", "message": "…"}}. The ones this endpoint can return:
bad_param(400): a value is the wrong type or out of range (the message says which)rate_limited(429): over 60 requests a minute; wait for Retry-After seconds
Questions
SAMEORIGIN vs frame-ancestors 'self'?
Same idea for simple sites: only the exact origin may embed the page. Prefer frame-ancestors — it can also allow specific partners, and modern browsers prioritise it when both headers are set.
Why does ALLOW-FROM not work in Chrome?
ALLOW-FROM was never implemented in Chromium and is ignored there (and in Firefox/Safari). Use Content-Security-Policy: frame-ancestors https://partner.example instead.
Can I set frame-ancestors in a meta tag?
No. Browsers ignore frame-ancestors, report-uri and sandbox when the policy comes from a meta tag. Send them as an HTTP response header.
Does default-src cover framing?
No. frame-ancestors is not a fetch directive and is never filled in by default-src. You have to set it yourself.
Related endpoints
/v1/csp/parseCSP header explainer: grade a Content-Security-Policy and check what it blocks/v1/cookie/parseSet-Cookie explainer: will the browser accept this cookie, and why not?/v1/http-statusLook up any HTTP status code: meaning, causes, fixes and retry advice
Prefer a page to an API?
- X-Frame-Options & frame-ancestors explainer with a framing simulator on Tooltern: same job, nothing leaves your browser.
Published · OpenAPI 3.1 spec · Limits and privacy