What is the X-Frame-Options header?
The X-Frame-Options response header tells a browser whether the response may be rendered inside a <frame>, <iframe>, <embed> or <object>. It was introduced to stop clickjacking, where an attacker frames your page invisibly and tricks the user into clicking on it. Despite the X- prefix, it is registered as a permanent field in the IANA HTTP Field Name Registry, with the WHATWG HTML Standard as its specification. RFC 7034 described it first, as an Informational RFC documenting existing practice.
Two values are valid:
| Value | Effect |
|---|---|
DENY |
The response may not be framed by anyone. |
SAMEORIGIN |
The response may be framed only if every ancestor frame, up to the top level, is same-origin with it. |
The HTML Standard defines exactly how browsers treat everything else. ALLOW-FROM, an old Internet Explorer extension, is not valid, and a lone invalid value is treated as if the header were absent, so ALLOW-FROM https://example.com allows framing from anywhere. Conflicting values such as SAMEORIGIN, DENY block framing. Values are case-insensitive, and the header has no effect in a <meta> element.
If the response also carries an enforced Content-Security-Policy with a frame-ancestors directive, the browser ignores X-Frame-Options entirely.
API Usage & Best Practices
- Send
DENYon API responses. A JSON response has no reason to be framed, and the header costs nothing. It only matters when a browser renders the response itself, but that is exactly the case an attacker tries to engineer. - Treat
frame-ancestorsas the real control.Content-Security-Policy: frame-ancestors 'none'is the modern equivalent ofDENY, takes precedence where both are present, and can express thingsX-Frame-Optionscannot, such as a list of allowed origins. Send both:frame-ancestorsfor current browsers,X-Frame-Optionsfor anything that predates it. The OWASP REST Security Cheat Sheet recommends the same pair for API responses. - Never send
ALLOW-FROM. Current browsers treat it as invalid, which means no protection at all. If you need to allow specific origins to frame a page, useframe-ancestors https://partner.example. - Send it once, with one value. Gateways and frameworks that both add the header can produce
DENY, SAMEORIGINor duplicated lines. Browsers resolve a conflict by blocking, which is safe, but it signals that two layers disagree about policy. Pick one layer to own it. - Exempt only what must be embeddable. An embeddable widget or a hosted payment form may genuinely need to be framed. Scope the exception to those routes with
frame-ancestorslisting the allowed origins, rather than dropping the header API-wide. - It means nothing to non-browser clients. SDKs and server-side callers ignore it.
Examples
The usual pairing on a JSON API response:
HTTP/1.1 200 OKContent-Type: application/jsonX-Frame-Options: DENYContent-Security-Policy: default-src 'none'; frame-ancestors 'none'A hosted page that the API’s own front end embeds, and nobody else:
HTTP/1.1 200 OKContent-Type: text/html; charset=utf-8X-Frame-Options: SAMEORIGINContent-Security-Policy: frame-ancestors 'self'