Explore how CSS-based clickjacking manipulates UI interactions and learn best practices for web security to prevent deceptive user clicks.
You think you’re clicking a “Play” button. You’re actually confirming a payment, granting a permission, or liking a page you’ve never heard of. That’s clickjacking, and the frustrating part is that the button you meant to press was real. An attacker just stacked something invisible on top of it and let your finger do the rest.
For years this trick leaned on JavaScript. These days a lot of it runs on CSS alone, which makes it quieter and easier to miss. Let’s walk through how CSS-based clickjacking actually works, what it can and can’t do, and the one defense that reliably shuts it down.
Table of Contents
- Understanding Clickjacking
- How CSS is Used in Clickjacking
- Techniques Used in CSS Clickjacking
- Advanced Clickjacking Methods
- Identifying Signs of Clickjacking
- Preventing Clickjacking Attacks
- The Bottom Line
Understanding Clickjacking
Clickjacking, also called UI redressing, tricks you into clicking something other than what you see. The usual setup loads a real, trusted page inside a transparent frame and floats it over bait you’ll want to click. You aim at the bait. The click lands on the real page underneath, doing whatever the attacker lined up: a “like”, a share, a permission grant, a purchase.
JavaScript used to do the heavy lifting because it can move things around on the fly. But a plain transparent iframe plus a bit of CSS is often enough, and skipping JavaScript has a bonus for the attacker: no script for script-blockers to catch, and less to trip your radar.
How CSS is Used in Clickjacking
CSS is what makes the target invisible and puts it exactly where you’ll click. Three properties do most of the work: opacity to make the frame see-through, z-index to keep it on top, and position to pin it over the spot you’re aiming at. The clickable area stays fully live the whole time. It’s just transparent.
Picture a real login or “confirm” page loaded in a near-invisible iframe, positioned over a big tempting button on the attacker’s own page. You see the button. Your click reaches the frame. That mismatch between what you see and what receives the click is the entire attack.
Techniques Used in CSS Clickjacking
The building blocks are simple, which is part of why this sticks around. Here are the common ones.
1. Transparent Overlays
Make an element invisible with opacity but keep it clickable, then park it over what the user thinks they’re pressing. Their click goes to the overlay, not the thing underneath.
Example of the code
/* Transparent overlay for clickjacking */
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: 9999;
}
The overlay class is fully transparent but still catches every click in its area. You think you’re touching the page below. You’re not.
2. Hidden Frames
Same idea, but the invisible layer is an iframe holding a real external page. Position it over a key part of your interface and it quietly collects clicks meant for something else.
Example of the code
/* Hidden iframe for clickjacking */
.hidden-frame {
position: absolute;
top: 10px;
left: 10px;
width: 300px;
height: 300px;
opacity: 0.01; /* Nearly invisible */
z-index: 9999;
}That near-zero opacity keeps the frame effectively invisible while it does its job. This is the classic shape of a clickjacking attack, and it’s exactly what the fix later in this post blocks.
3. Forced Button Clicks Using Z-Index
z-index decides what sits on top. Crank it up and an invisible element wins every stacking contest, so it grabs the click no matter what’s drawn under it. This is the go-to for baiting “like” or “subscribe”.
Example of the code
/* Forced button click using z-index */
.click-target {
position: absolute;
top: 100px;
left: 100px;
width: 150px;
height: 50px;
z-index: 1000; /* Higher than other elements */
}
Drop .click-target on top of real controls and clicks get trapped by the layer you can’t see.
4. Cursor Manipulation
A niche one, sometimes called cursorjacking. CSS can hide the real cursor so you misjudge where you’re actually pointing. It’s more fiddly and less reliable than the framing tricks, but it’s in the toolkit.
Example of the code
/* Hiding the cursor on hover */
.hidden-cursor {
cursor: none;
}Hide the cursor and you lose your best cue for where the click will land, which is enough to nudge someone onto a hidden target.
Advanced Clickjacking Methods
The pattern scales up. A few variations worth knowing about:
Chained Clickjacking
Instead of one hidden click, the attacker walks you through a sequence of invisible frames, each click doing a separate job. Spread across a few steps, it’s harder to notice something’s off.
Multi-Layering with Dynamic Frames
Frames that appear in response to what you do. Click one overlay and it reveals the next, pulling you deeper into the sequence one interaction at a time.
Conditional Clickjacking
The attack only shows up under set conditions, like a certain screen size or browser, so it stays hidden from anyone who doesn’t match and harder to reproduce when you go looking.
Identifying Signs of Clickjacking
Spotting this is genuinely hard, especially with no JavaScript to inspect. A few things worth watching for:
- Strange overlays: unexpected layers during normal use, or elements that block or lag interactions they shouldn’t.
- The cursor disappearing: if it vanishes over certain spots, something may be hiding what you’re really pointing at.
- Odd elements in DevTools: open your browser tools and look for very high
z-indexvalues or near-zero opacity on things that shouldn’t have either.
Preventing Clickjacking Attacks
Here’s the honest part: CSS and JavaScript tricks on your side won’t reliably stop this. Frame-busting scripts get bypassed, and you can’t out-style an attacker who controls their own page. The fix that actually holds is a server response header that tells the browser your page may not be framed. Set it on the server, not in the markup.
- Content Security Policy with
frame-ancestors(the modern default): a CSP header likeContent-Security-Policy: frame-ancestors 'none'tells browsers who, if anyone, is allowed to embed your page. This is the current, more flexible defense, and it’s the one to reach for first. X-Frame-Options(the legacy fallback): the older header, usingX-Frame-Options: DENYorSAMEORIGIN. Still worth sending for older browsers that don’t honor CSP, but treat it as a backup, not your main line. Skip the oldALLOW-FROMvalue entirely; it’s obsolete and unsupported in modern browsers.- Limit framing: only frame content from sources you trust, and only when you actually need to.
- Tell people what to watch for: users and clients who know clickjacking exists are a little harder to fool. It’s a soft layer, not a substitute for the headers above.
The Bottom Line
CSS-based clickjacking works by hiding a real action under something you actually meant to click. With opacity, z-index, and position it can slip past defenses that only watch for scripts, which is what makes it quiet and effective. Knowing the shape of it is the first step to shutting it down.
The good news is the reliable defense is small and boring: send frame-ancestors via CSP, add X-Frame-Options for older browsers, and don’t lean on client-side tricks to do a header’s job. Set those, check them in a real audit, and this whole class of attack mostly stops being your problem.


