← All fixes

Fix it

Missing clickjacking protection (X-Frame-Options), how to fix it

Medium severityCWE-1021 (Improper Restriction of Rendered UI Layers)
We scanned nearly 2,000 AI-built apps and 1 in 8 shipped a high-severity flaw. Is this one in yours?Scan free →

Clickjacking is when an attacker loads your site inside an invisible frame on their own page and tricks a logged-in user into clicking something they cannot see, approving an action, changing a setting, confirming a payment. The defense is to tell browsers your site may not be framed by other origins, with X-Frame-Options or a Content-Security-Policy frame-ancestors directive.

Why it's a problem

Because the framed page is your real, authenticated site, the clicks land as genuine user actions. The attacker overlays their own bait over your buttons, so the user thinks they are clicking one thing while actually clicking another on your site. Any state-changing action, from settings to transfers, can be hijacked this way.

The pattern

// no framing protection — any site can embed yours
app.get("/", (req, res) => res.send(html));

The fix

// block framing by other origins
app.use((req, res, next) => {
  res.setHeader("X-Frame-Options", "DENY");
  res.setHeader(
    "Content-Security-Policy",
    "frame-ancestors 'none'"
  );
  next();
});

Why AI tools write this

Framing protection is a response header nothing in a feature prompt asks for, and the app works identically with or without it. So generated apps ship without it, leaving the clickjacking surface open by default.

The quick fix

  • Set X-Frame-Options: DENY (or SAMEORIGIN if you frame your own pages).
  • Add a Content-Security-Policy with frame-ancestors 'none' as the modern equivalent.
  • Apply these headers globally, at the framework or CDN level.

Common questions

Does every page need clickjacking protection or just login pages?

Any page with state-changing actions needs it. Apply protection globally at the framework or CDN level rather than page by page to ensure nothing is missed.

Is X-Frame-Options deprecated?

It is not deprecated but is considered legacy. The modern replacement is CSP frame-ancestors, which is more flexible and overrides X-Frame-Options in supporting browsers. Set both for maximum compatibility.

Does this protection affect embedded chat widgets or third-party frames I want on my site?

If you use SAMEORIGIN instead of DENY, pages on your own domain can frame each other. To allow a specific third-party to embed your content, use frame-ancestors with that origin listed explicitly instead of DENY.

Want to know if this pattern is already in something you shipped? Scan your live app or a public repo free, no account needed.

Scan my app →

Related: HTTP security headers

Catch this automatically: scan your GitHub repo · website vulnerability scanner · review every pull request · SAST for AI code · OWASP Top 10 for AI code

Clickjacking: Missing X-Frame-Options / frame-ancestors, The Fix | Prbl