TL;DR
Clickjacking embeds your site in an invisible iframe on a malicious page. Users think they're clicking something on the attacker's page but actually click buttons on your hidden site. Prevent it by adding X-Frame-Options: DENY or Content-Security-Policy: frame-ancestors 'none' headers.
What Is Clickjacking?
Clickjacking (also called UI redressing) tricks users into clicking something different from what they perceive. Attackers embed your legitimate page in an invisible iframe and overlay their own UI elements positioned exactly where your buttons are.
How Clickjacking Works
<!-- Attacker's malicious page -->
<style>
iframe {
position: absolute;
opacity: 0; /* Invisible! */
z-index: 2;
}
.fake-button {
position: absolute;
top: 100px;
left: 200px; /* Positioned over your real button */
}
</style>
<p>Click here to win a prize!</p>
<div class="fake-button">CLAIM PRIZE</div>
<iframe src="https://yoursite.com/delete-account"></iframe>
<!-- User clicks "CLAIM PRIZE" but actually clicks "Delete Account" on your site -->
How to Prevent Clickjacking
| Header | Value | Effect |
|---|---|---|
| X-Frame-Options | DENY | Cannot be framed by any site |
| X-Frame-Options | SAMEORIGIN | Only your domain can frame it |
| Content-Security-Policy | frame-ancestors 'none' | Modern alternative to X-Frame-Options |
// next.config.js
const securityHeaders = [
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'Content-Security-Policy',
value: "frame-ancestors 'none'"
}
];
module.exports = {
async headers() {
return [{ source: '/:path*', headers: securityHeaders }];
}
};
Should I use X-Frame-Options or CSP frame-ancestors?
Use both for maximum compatibility. X-Frame-Options works in older browsers, while CSP frame-ancestors is the modern standard and offers more flexibility.
What if I need to embed my site somewhere?
Use CSP frame-ancestors with specific allowed domains: frame-ancestors 'self' https://trusted-partner.com