X-Frame-Options
Usage
Clickjacking attacks trick users into clicking hidden elements layered over a legitimate page. An attacker embeds the target site in a transparent iframe, positions a deceptive UI on top, and captures clicks intended for the framed content. X-Frame-Options prevents this by telling the browser whether the response is allowed to appear inside a frame.
The header accepts two active values: DENY and SAMEORIGIN. A third value, ALLOW-FROM, was defined in earlier implementations but is now obsolete and ignored by modern browsers.
DENY
The DENY value blocks the page from being displayed in any frame, regardless of the requesting site. No framing is permitted under any circumstance.
SAMEORIGIN
The SAMEORIGIN value allows the page to be framed only by pages sharing the same origin. A page on https://example.re is allowed to frame another page from https://example.re, but a page on a different origin is blocked.
ALLOW-FROM (obsolete)
The ALLOW-FROM uri value once instructed the browser to permit framing only from the specified URI. Modern browsers no longer support this directive. The equivalent functionality is available through the CSP frame-ancestors directive:
Content-Security-Policy: frame-ancestors https://trusted.example.re
Example
Blocking all framing of a page, the strictest protection against clickjacking:
X-Frame-Options: DENY
Allowing same-origin framing while blocking external sites from embedding the page:
X-Frame-Options: SAMEORIGIN
A defense-in-depth configuration combining X-Frame-Options with the CSP frame-ancestors directive for broad browser coverage:
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
Troubleshooting
Framing issues show up as blank iframes or console errors when a page refuses to render inside another document.
“Refused to display in a frame” console error. The browser blocks the framed page and logs Refused to display ‘https://example.re/’ in a frame because it set ‘X-Frame-Options’ to ‘DENY’ (or ‘SAMEORIGIN’). Check the response headers of the framed URL in the DevTools Network tab. If the page needs to be embedded, change the header value from DENY to SAMEORIGIN or remove the header entirely and use CSP frame-ancestors for finer control.
ALLOW-FROM not working in modern browsers. The ALLOW-FROM directive is obsolete. Chrome, Firefox, and Safari ignore the directive entirely, treating the header as if no valid value were set. Replace ALLOW-FROM with the CSP frame-ancestors directive, which supports multiple origins: Content-Security-Policy: frame-ancestors https://trusted.example.re https://other.example.re.
Conflicting X-Frame-Options and CSP frame-ancestors. When both headers are present, browsers that support CSP Level 2 and later ignore X-Frame-Options and follow frame-ancestors only. Older browsers that lack CSP support fall back to X-Frame-Options. Confirm the intended policy is consistent across both headers. Sending X-Frame-Options: DENY alongside frame-ancestors ‘self’ creates a mismatch where modern browsers allow same-origin framing but legacy browsers block all framing.
Legitimate embedding blocked (payment forms, widgets, embedded apps). Third-party services requiring iframe embedding fail when the host page sends X-Frame-Options: DENY. Use CSP frame-ancestors to allowlist specific origins that need embedding access: Content-Security-Policy: frame-ancestors ‘self’ https://payments.provider.example.re.
Migrating from X-Frame-Options to CSP frame-ancestors. Deploy both headers during the transition period. Set frame-ancestors to match the intended framing policy. Keep X-Frame-Options for browsers without CSP support. Once legacy browser traffic drops below acceptable thresholds, remove X-Frame-Options and rely on frame-ancestors alone. Test the migration by embedding the page in an iframe from an allowed origin and verifying the frame loads in multiple browsers.