Glossary

What is clickjacking? An attack that makes you click buttons on a transparent page laid over another

Clickjacking overlays a real page invisibly on top of an attacker's site so a logged-in user clicks 'hidden' buttons. How it works, and the main defense (CSP frame-ancestors and X-Frame-Options so your site can't be framed) — explained defensively, no attack steps.

Published 2026-06-10 Updated 2026-10-06 7 min read
On this page

Clickjacking tricks you into clicking a button on a hidden page underneath the one you see. Here's how it works and how to reliably prevent it (no attack steps).

What gets targeted

Anything that is "one click, done, and valuable."

Likely targetWhy
Confirming a transfer/purchaseOne click moves money
Settings change (visibility, connected apps)Sets up later takeover or data access
OAuth / permission "Allow"Silently approves an account link
Social follow / like / postAbused for spread and like-farming

Why it works

Browsers can load another site in an iframe and stack it on top. The attacker makes that iframe transparent (opacity 0), so the user only sees the decoy UI placed below. When the user clicks the "fake button," the real button layered directly above gets the click.

Attacker page: "You won a prize — click below!"
= a transparent iframe of the real bank/settings page on top
↓ user clicks the visible decoy button
the real "Confirm" button directly above is what's actually clicked
↓ under the user's own login
transfer / settings change / consent completes as "the user's action"
The attacker stacks the real site invisibly; only the decoy UI is visible. The click goes to the transparent real page.

It resembles CSRF, but CSRF sends a request behind the scenes, while clickjacking makes the user operate the real screen — so CSRF tokens alone don't stop it.

Real case: a browser plugin's permission dialog (2008)

In October 2008, Adobe published a security advisory (APSA08-08) about a clickjacking issue affecting multiple browsers. According to press reports, by layering the Flash Player settings dialog invisibly, a site could get a user to grant it access to the camera and microphone without realizing. Adobe released fixed versions of Flash Player and, as an interim workaround, advised users to choose "always deny" in the Settings Manager.

The lesson: transfer buttons aren't the only target. Any screen where a single click hands over a permission ("Allow," "Agree," "Connect") is in scope. MITRE classifies this weakness as CWE-1021 (Improper Restriction of Rendered UI Layers or Frames).

Defense: don't let other sites embed your site

1

Set CSP frame-ancestors (most important)

Add Content-Security-Policy: frame-ancestors 'self' (only your own origin may frame you). If no one needs to embed you, 'none' is safest. List domains only for the partners you explicitly allow.

2

Add X-Frame-Options too (backward compatible)

For older clients, also send X-Frame-Options: DENY (or SAMEORIGIN). This covers both old and new browsers.

3

Add a confirmation step for critical actions

Gate transfers and permission grants behind re-auth, a confirm dialog, or an intent check — harder to drive through a silent overlay.

4

Make session cookies SameSite

SameSite=Lax/Strict curbs cross-site auto-sending. Not a complete clickjacking fix on its own, but it weakens the broader "action started from another site" class.

X-Frame-Options vs CSP frame-ancestors

Both headers stop other sites from embedding yours, but they differ in the details.

X-Frame-OptionsCSP frame-ancestors
ValuesDENY (block all) / SAMEORIGIN (same origin only)'none' / 'self' / a list of allowed domains
Multiple allowed embeddersNot possible (ALLOW-FROM doesn't work in modern browsers)Possible
Set via meta tagNo effectNo effect
When both are presentIgnored by supporting browsersTakes precedence
RoleFallback for older browsersThe main defense today

When sending both, keep their meaning aligned: pair frame-ancestors 'none' with DENY, and frame-ancestors 'self' with SAMEORIGIN.

Common configuration mistakes

  • Assuming CSP default-src covers it. frame-ancestors does not fall back to default-src. Even with default-src 'none', embedding stays allowed unless you write frame-ancestors
  • Putting it in a meta tag: <meta http-equiv> has no effect here, so send it as a response header
  • Leaving it as Content-Security-Policy-Report-Only: that only reports violations and doesn't block embedding. Switch to the enforcing header once you've confirmed it works
  • Setting it on only some pages: people check the home page and miss the login page, settings pages, or error pages. Also confirm a CDN or proxy isn't stripping the header

How to check your own site

1

Check with a headers tool

Enter your site's URL in the security headers checker and see whether X-Frame-Options and CSP frame-ancestors are returned. Check not just the home page but also logged-in settings pages and the pages for transfers or permission grants.

2

Check in the browser's developer tools

Reload the page with the Network tab open and look at the HTML response headers. Also check that the header isn't sent twice or with conflicting values.

3

Set it in one place

Adding it to every response in one place (the web server such as nginx or Caddy, the reverse proxy, or the framework) prevents both gaps and duplicates. If you're writing a new CSP, the CSP builder can assemble one that includes frame-ancestors.

This site's view: one header line is the most effective fix, so check your own site first

Clickjacking defense isn't complex code; it's one or two response-header lines that block embedding. Apply them site-wide at the framework or reverse proxy (Caddy/nginx) so nothing slips through. The most common failure is a header you thought you had set but is actually missing, so after setting it, confirm with the steps above that it's really being returned.

Sources (primary)

  • OWASP, "Clickjacking Defense Cheat Sheet" — cheatsheetseries.owasp.org (frame-ancestors, X-Frame-Options, SameSite; ALLOW-FROM no longer works; meta tags have no effect; limits of frame busting)
  • MDN, "CSP: frame-ancestors" — developer.mozilla.org (allowed values, no default-src fallback, not supported in meta)
  • RFC 7034, "HTTP Header Field X-Frame-Options" — rfc-editor.org
  • IPA, "How to Secure Your Web Site, 1.9 Clickjacking" (Japanese) — ipa.go.jp
  • Adobe, "Security Advisory for Flash Player (APSA08-08)" (October 7, 2008) — adobe.com
  • Computerworld, "Clickjackers could hijack webcams, microphones, Adobe warns" (October 2008) — computerworld.com (the camera and microphone permission dialog was the target)
  • MITRE, "CWE-1021: Improper Restriction of Rendered UI Layers or Frames" — cwe.mitre.org

Classification

CWE classifies the types of weakness behind vulnerabilities; MITRE ATT&CK classifies attacker techniques. Both are maintained by MITRE and widely used to label vulnerability reports and detection rules.

CWE™: Copyright © 2006–2026, The MITRE Corporation. CWE is a trademark of The MITRE Corporation. The MITRE Corporation hereby grants you a non-exclusive, royalty-free license to use CWE for research, development, and commercial purposes. Any copy you make for such purposes is authorized on the condition that you reproduce MITRE’s copyright designation and this license in any such copy. CWE Terms of Use

FAQ

QWhat can clickjacking do?
A

A click the user makes 'on purpose' lands on a real page's button layered underneath. One-click critical actions get abused: money transfers, settings changes, granting permissions, social follow/like, consent buttons. It doesn't steal a password — it makes the already-logged-in user perform the action.

QWhat's the top defense?
A

Refuse to be embedded in another site's frame. Set the CSP frame-ancestors directive to 'self' (or only allowed domains) in your response headers, and add X-Frame-Options: DENY for backward compatibility. That removes the surface an overlay needs.

QIs JavaScript frame-busting enough?
A

No. Old 'break out if I'm in an iframe' scripts have many bypasses and don't help when JS is disabled. The reliable defense is server headers (frame-ancestors / X-Frame-Options) that tell the browser to refuse the embed.

QX-Frame-Options or CSP frame-ancestors: which should I use?
A

Send both with matching meaning; that's the standard practice. frame-ancestors can list multiple allowed embedders, and when both are present, browsers that support it apply frame-ancestors. X-Frame-Options is a fallback for older browsers. Pair 'none' with DENY and 'self' with SAMEORIGIN.

QCan I set it with an HTML meta tag?
A

No. frame-ancestors has no effect in a meta element, and X-Frame-Options in a meta element is ignored too. Both must be sent as HTTP response headers, set in your web server, reverse proxy, or framework.

QWhat if I need to allow my other sites or a partner to embed pages?
A

List the allowed parties in frame-ancestors (for example, frame-ancestors 'self' https://partner.example). X-Frame-Options' ALLOW-FROM doesn't work in modern browsers, so don't rely on it for an allow list. Limit the permission to the pages and domains that genuinely need embedding.