How to set X-Frame-Options correctly?
x-frame-options-incorrect page
The issue
A page that any site can frame can be laid under a decoy, so a visitor’s clicks land on buttons they can’t see: clickjacking. X-Frame-Options is the older header that stops this, and a missing or invalid value leaves the page frameable.
The cause is usually a missing header or ALLOW-FROM, which current browsers ignore. Only DENY and SAMEORIGIN are reliable.
Scope
The header is set per response and matters on HTML pages, above all those where a click changes something. Probeo doesn’t grade X-Frame-Options. It checks framing through the Content Security Policy’s frame-ancestors directive, which current browsers use in preference.
How to check you have it
- In probeo: a page whose Content Security Policy has no
frame-ancestorsdirective is recorded ascsp:missing-frame-ancestors. A page with no policy at all is recorded ascsp-missing. - By hand: run
curl -sI https://example.com/and look forx-frame-optionsand forframe-ancestorsincontent-security-policy.
The fix
Set X-Frame-Options to DENY, or to SAMEORIGIN if the site frames its own pages.
Use DENY for login, account and payment pages. Add frame-ancestors to the Content Security Policy with the matching value, 'none' or 'self', since that is the directive current browsers enforce.
Done when: every HTML page sends DENY or SAMEORIGIN, the policy’s frame-ancestors matches it, and the check above comes back clean.
Do not
- Do not use
ALLOW-FROM; current browsers ignore it. - Do not use values browsers don’t recognize.
- Do not set
SAMEORIGINwhere nothing on the site frames the page.
FAQ
Should most pages use DENY or SAMEORIGIN?
DENY unless same-origin framing is required. Most pages never need iframe embedding. Login pages, account management, and sensitive operations should always use DENY. Content pages can use SAMEORIGIN if the application legitimately embeds its own pages.
Why does ALLOW-FROM not work reliably?
Browser support was always limited and most browsers now ignore it. Chrome never fully implemented ALLOW-FROM. Firefox and Edge removed support. The directive provides no functional protection in modern browsers. Use CSP frame-ancestors for equivalent functionality with broad support.
Can X-Frame-Options and frame-ancestors coexist?
Yes. Set both for defense in depth and backward compatibility. Modern browsers prioritize frame-ancestors when both are present. Older browsers that lack CSP support still respect X-Frame-Options. Consistent values across both headers prevent confusion.
Does X-Frame-Options affect window.open or popups?
No. The header controls only iframe, frame, and object embedding. Window.open creates new top-level contexts that are not affected. Popups and new windows do not inherit or respect X-Frame-Options from the opener.
What breaks when DENY is set but pages are embedded?
Legitimate iframe embedding stops working. Third-party integrations that embed pages fail. OAuth flows using iframes break. Internal applications that frame content stop functioning. Audit embedding requirements before deploying DENY globally. Sensitive pages should use DENY regardless.
Should static assets set X-Frame-Options?
Not typically necessary. Clickjacking targets interactive pages where user actions matter. Images, scripts, and stylesheets do not process user actions. Focus X-Frame-Options on HTML pages, especially those requiring authentication. Static assets can omit the header without security impact.
Which product detects this
Probeo checks this on every page of your site as part of Site Clarity. This page explains one instance; the product tells you every page that has it.