X-Frame-Options and Clickjacking Protection

Clickjacking tricks your users into clicking something they cannot see. The defence is simple: tell browsers who is allowed to embed your pages inside a frame. Two headers do that job — the older X-Frame-Options and the modern CSP frame-ancestors directive.

The short version

Set X-Frame-Options: SAMEORIGIN for broad browser coverage, and add frame-ancestors to your Content Security Policy for the modern, more flexible version of the same protection. If you set both, frame-ancestors wins in current browsers. Confirm what your site sends with our headers check.

How clickjacking works

In a clickjacking attack, the attacker loads your real site inside a transparent or disguised <iframe> on a page they control. They then layer their own content on top and position an invisible copy of your page so that when the victim clicks what looks like an innocent button, they are actually clicking a control on your site — confirming a payment, changing a setting, or approving a permission. Because the click lands on your genuine page while the user is logged in, the action is real. The user never sees your site at all.

The fix is to stop your pages from being framed by sites you do not trust. A browser can only honour that instruction if you send it, which is why an unframed-by-default policy is a baseline security header.

Anywhere a logged-in click has a real effect is a target: banking and payment confirmations, admin toggles, one-click purchases, follow or subscribe buttons, and any “grant access” prompt. The attacker does not need to break your authentication — they borrow the victim’s existing session and simply steer the click. That is what makes framing protection worth applying across your whole site rather than only on a login page.

X-Frame-Options

This header takes one of two useful values:

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
ValueEffect
DENYNo site may frame your pages at all, not even your own.
SAMEORIGINOnly pages from your own origin may frame yours. The safe default for most sites.

An older ALLOW-FROM value existed for naming a single permitted origin, but it was inconsistently supported and is now obsolete. If you need to allow specific external embedders, use CSP instead.

The modern replacement: frame-ancestors

The frame-ancestors directive of Content Security Policy does everything X-Frame-Options does and more. It accepts a list of allowed origins rather than a single one:

Content-Security-Policy: frame-ancestors 'none'
Content-Security-Policy: frame-ancestors 'self'
Content-Security-Policy: frame-ancestors 'self' https://partner.example.com

Here 'none' is equivalent to DENY, 'self' is equivalent to SAMEORIGIN, and you can add explicit trusted origins — useful when a genuine partner or your own help-desk tool legitimately embeds your pages.

frame-ancestors supersedes X-Frame-Options. When a browser understands CSP frame-ancestors, it ignores X-Frame-Options entirely. Make sure the two agree. Setting X-Frame-Options: DENY but frame-ancestors 'self' will allow same-origin framing in modern browsers, which may not be what you intended.

How to test it

Confirming the protection works is simple. Build a throwaway HTML page on a different origin that tries to frame one of your pages:

<iframe src="https://example.com/account"></iframe>

Open it and watch the browser console. With the header in place the frame stays blank and the browser logs a refusal to display the page because of the framing rule. You can also inspect the response headers directly in your browser’s network panel, or use an automated headers check to see the value without hand-testing.

Which should you use?

Send both. Keep X-Frame-Options: SAMEORIGIN as a fallback for older clients, and add a matching frame-ancestors to your CSP so modern browsers get the more precise rule. The two together cover essentially every browser your visitors use.

X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'

On Apache, add these with Header always set; on Nginx, with add_header ... always;. If your site is a pure API or is never meant to be embedded, prefer DENY and 'none'. Both belong on the wider security headers checklist — once deployed, run a scan to confirm your pages can no longer be framed by strangers.

Frequently asked questions

Will this break legitimate embeds of my site?

It can. If a real partner embeds your pages, list their origin in frame-ancestors rather than removing the protection. Never fall back to allowing all origins.

Do I still need X-Frame-Options if I use CSP?

It is worth keeping as a fallback for the small number of older clients that do not support frame-ancestors. Modern browsers will use the CSP directive and ignore the older header.

Does this protect against every clickjacking variant?

Frame-busting headers stop the core iframe technique, which covers the vast majority of attacks. Combine them with sensible confirmation steps on high-risk actions for defence in depth.

Related guides

Check your site against this guide

Run a free ScanOpsPro scan and see how your site handles the fundamentals.

Run a free scan