Skip to main content
Back to blog
April 28, 2026|8 min read|Antoine Duno

Clickjacking Prevention: X-Frame-Options, CSP & UI Redressing

Clickjacking prevention starts with two HTTP headers that most sites still get wrong. This guide explains X-Frame-Options, CSP frame-ancestors, and how UI redressing attacks work.

ZeriFlow Team

1,398 words

Clickjacking Prevention: X-Frame-Options, CSP & UI Redressing

Clickjacking prevention is a critical yet often overlooked aspect of web application security. A clickjacking attack — also called a UI redressing attack — tricks users into clicking on something different from what they perceive, by overlaying a transparent iframe over a legitimate-looking UI. One click can unknowingly authorize a payment, change account settings, or enable a webcam.

Find out if your site is vulnerable to clickjacking in seconds — [scan it free with ZeriFlow](https://zeriflow.com).


How Clickjacking Attacks Work

The mechanics are deceptively simple. An attacker creates a malicious page that loads your web application inside an invisible or transparent <iframe>. They then overlay decoy content — a fake button, a game element, a social share prompt — precisely over a sensitive action on your framed page.

html
<!-- Attacker's malicious page -->
<style>
  iframe {
    position: absolute;
    top: 0; left: 0;
    width: 100%; height: 100%;
    opacity: 0.0001; /* invisible but clickable */
    z-index: 2;
  }
  .decoy-button {
    position: absolute;
    top: 200px; left: 300px;
    z-index: 1;
  }
</style>

<div class='decoy-button'>Click here to claim your prize!</div>
<iframe src='https://victim-bank.com/transfer?to=attacker&amount=1000'></iframe>

When the victim clicks the "prize" button, they're actually clicking the bank's transfer confirmation button on the transparent iframe.

Real-World Clickjacking Scenarios

  • Like farming: Framing a Facebook Like button to drive fake engagement.
  • Webcam/microphone activation: Clickjacking Adobe Flash permission dialogs (historically common).
  • Account deletion: Tricking users into clicking "delete my account" confirmations.
  • OAuth authorization: Framing an OAuth consent screen to silently grant app permissions.
  • Form submissions: Pre-filling and framing forms to extract submitted data.

X-Frame-Options: The Original Defense

X-Frame-Options is an HTTP response header that controls whether a browser is allowed to render a page inside a <frame>, <iframe>, <embed>, or <object>.

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
X-Frame-Options: ALLOW-FROM https://trusted.com

Directive Reference

DirectiveMeaning
DENYPage cannot be framed by anyone
SAMEORIGINPage can only be framed by pages on the same origin
ALLOW-FROM uriPage can be framed only by the specified URI

Recommendation: Use DENY for all pages that don't need to be embedded. Use SAMEORIGIN if your own app embeds its pages. Avoid ALLOW-FROM — it's deprecated and not supported in Chrome or Firefox.

Setting X-Frame-Options

Nginx:

nginx
add_header X-Frame-Options 'DENY' always;

Apache:

apache
Header always set X-Frame-Options "DENY"

Express.js (Node.js):

javascript
const helmet = require('helmet');
app.use(helmet.frameguard({ action: 'deny' }));

Django:

python
# settings.py
X_FRAME_OPTIONS = 'DENY'
MIDDLEWARE = [
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # ...
]

CSP frame-ancestors: The Modern Standard

While X-Frame-Options is widely supported and still effective, Content Security Policy (CSP) `frame-ancestors` is the modern, more flexible replacement. It's part of the CSP Level 2 specification and takes precedence over X-Frame-Options in browsers that support it.

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

Why CSP frame-ancestors Is Superior

  1. 1Multiple allowed origins: ALLOW-FROM in X-Frame-Options only supports one URI. CSP frame-ancestors supports multiple.
  2. 2Wildcard support: frame-ancestors 'self' https://*.trusted.com allows all subdomains.
  3. 3More granular control: Combine with port and scheme specifications.
  4. 4CSP violation reporting: Get reports when framing is attempted.
Content-Security-Policy: frame-ancestors 'self' https://app.partner.com; report-uri /csp-violations

Deploying Both Headers

For maximum compatibility (older browsers, IE), deploy both headers:

nginx
add_header X-Frame-Options 'DENY' always;
add_header Content-Security-Policy "frame-ancestors 'none'" always;

CSP frame-ancestors takes precedence in modern browsers; X-Frame-Options covers the rest.


ZeriFlow Checks Both Headers

ZeriFlow's security scanner automatically checks for:

  • Presence of X-Frame-Options header
  • Presence and correctness of frame-ancestors in your CSP header
  • Conflicting configurations between the two

Run a free clickjacking scan on your site at ZeriFlow — results in under 60 seconds, no signup required.


JavaScript Frame-Busting: An Unreliable Fallback

Before X-Frame-Options existed, developers used JavaScript "frame-busting" scripts to detect when their page was framed and redirect to the top-level window:

javascript
// Old-school frame-busting (DO NOT rely on this)
if (top !== self) {
  top.location = self.location;
}

This is easily defeated. Attackers can sandbox the iframe to disable JavaScript:

html
<iframe src='victim.com' sandbox='allow-forms allow-scripts'></iframe>

Or use onBeforeUnload tricks. Never rely on JavaScript frame-busting as your primary defense. Use HTTP headers.


Advanced Clickjacking Techniques

Drag-and-Drop Clickjacking

Some attacks use HTML5 drag-and-drop events rather than clicks. The user drags an element on the attacker's page but is actually interacting with a framed target. This can be used to extract text content from framed cross-origin pages.

Mitigation: Same X-Frame-Options/frame-ancestors headers prevent the frame from loading at all.

Multi-Step Clickjacking

Instead of a single click, attackers guide users through multiple interactions — each one triggering a step in a multi-page workflow (like a multi-step bank transfer confirmation). The attacker needs to precisely align each step.

Mitigation: CSRF tokens on each step make it much harder to craft a working multi-step attack even if framing succeeds.

Cursor Jacking

Attackers can manipulate the apparent cursor position using CSS, making users think their cursor is in one location while it's actually elsewhere. Combined with clickjacking, this is particularly deceptive.


Common Clickjacking Mistakes

1. Only protecting login pages Sensitive actions exist throughout your app — password changes, payment confirmations, permission grants. Apply framing protection to all pages, not just login.

2. Using X-Frame-Options on CDN assets Static assets (images, scripts) don't need framing protection. Apply headers selectively to HTML responses.

3. Setting SAMEORIGIN when DENY is appropriate If your app doesn't use iframes internally, use DENY. SAMEORIGIN only makes sense if you deliberately embed your own pages.

4. Forgetting the `always` flag in Nginx/Apache Without always, the header isn't sent on error responses (4xx, 5xx). Attackers can sometimes frame error pages to extract information.

5. Not testing after deployment Header misconfiguration is common. Tools like ZeriFlow can catch this automatically.


FAQ

Q: Does HTTPS prevent clickjacking?

A: No. HTTPS secures the transport layer but has no bearing on whether your page can be framed. Clickjacking attacks work perfectly over HTTPS. You need X-Frame-Options or CSP frame-ancestors headers regardless of your SSL configuration.

Q: Will blocking iframes break my legitimate embed functionality?

A: If you need your site to be embeddable (a widget, a map, a video player), use CSP frame-ancestors with specific allowed origins rather than DENY. This gives you fine-grained control — blocking unauthorized framing while allowing trusted partners.

Q: Can mobile apps be clickjacked?

A: Native mobile apps aren't affected since they don't use browser iframes. However, mobile web apps (running in a browser or WebView) are vulnerable to the same clickjacking attacks as desktop web apps. Apply the same header-based protections.

Q: Is clickjacking in the OWASP Top 10?

A: Clickjacking falls under OWASP A05:2021 (Security Misconfiguration) in the current Top 10. It's also historically noted as a significant vulnerability in the OWASP Testing Guide. While not always ranked #1, it's consistently considered a high-priority misconfiguration to fix.

Q: How do I test if my site is vulnerable to clickjacking?

A: The simplest test is to create an HTML file with an iframe pointing to your site and open it in a browser. If the page loads in the iframe, you're vulnerable. Automated tools like ZeriFlow check this instantly, along with 80+ other security issues.


Conclusion

Clickjacking prevention is one of the highest-ROI security fixes you can make — two HTTP headers eliminate the attack surface entirely. X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none' should be in your default server configuration for every web application.

The gap between knowing about clickjacking and actually being protected is often just a missing header. [Scan your site with ZeriFlow right now](https://zeriflow.com) to see if you're protected — it takes less than a minute and covers 80+ security checks beyond just clickjacking.

Don't let a two-line fix be the reason a user's account gets compromised.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading