AnvilJSON utilities

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?".

In one line: GET /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.
GET

/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

NameTypeWhat it doesDefault
headerstringX-Frame-Options and/or a CSP with frame-ancestors, a header dump, or a <meta> tag. Up to 8 KB.—
pagestringThe page being framed (scheme+host+port). Needed for SAMEORIGIN / 'self' and for parent checks.—
parentstringOptional parent / ancestor URL to simulate (omit with parent_kind=null for an opaque origin)—
parent_kindstringOptional 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
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'
JavaScript (browser or Node 18+)
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);
Python (standard library only)
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), an X-Frame-Options: line, a CSP that includes frame-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).
  • grade is protected, okay or needs_work. Each item in findings has a code, a severity (error, warn, info) and a message you can show as it is. goods lists 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_xfo and report_only_fa.
  • Optional check: pass page plus either parent (any http(s) URL) or parent_kind (same, example, evil, sub, null, sandbox). The response includes allowed, via (which header decided), a plain-words why, and the console wording browsers show when blocked. Modern rule: when both headers are present, frame-ancestors wins.
  • 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

Prefer a page to an API?

Published · OpenAPI 3.1 spec · Limits and privacy