Guides6 min read18 October 2026

How to Prevent Clickjacking on Your Website

Clickjacking embeds your site in an invisible iframe to trick users into clicking buttons they cannot see. This guide explains how to detect and prevent it with X-Frame-Options and CSP.

By Yrzo AI — UK cybersecurity specialists

How Clickjacking Works

Clickjacking — also called a UI redress attack — works by overlaying a transparent iframe of your website on top of a page the attacker controls. The victim sees the attacker's page and believes they are clicking a button presented on that page. In reality, their clicks land on your site, loaded invisibly underneath.

A straightforward example: an attacker builds a page with a "Click here to win a prize" button. Your website's account deletion confirmation button is positioned directly beneath it in a transparent iframe. When the victim clicks the prize button, they actually click your delete account button — while authenticated in your application.

The attack requires no vulnerability in your application code. It exploits the browser's willingness to load your site inside an iframe on any domain unless you explicitly instruct it otherwise.

The Fix: Frame-Ancestors Controls

Option 1: X-Frame-Options Header

The `X-Frame-Options` response header tells the browser whether your page is permitted to be loaded in an iframe, frame, or object element.

``` X-Frame-Options: DENY ```

`DENY` prevents your page from being framed by any site, including your own. This is the correct setting for the majority of websites.

``` X-Frame-Options: SAMEORIGIN ```

`SAMEORIGIN` allows framing only by pages on the same origin (same scheme, host, and port). Use this if your own application legitimately embeds pages in iframes — for example, a dashboard that displays a report in an embedded frame.

`X-Frame-Options: ALLOW-FROM uri` existed to whitelist a specific origin, but it is deprecated and inconsistently supported across browsers. Do not use it.

Option 2: Content-Security-Policy frame-ancestors Directive

The `frame-ancestors` directive in the Content-Security-Policy header is the modern replacement for `X-Frame-Options`. It is more flexible, supports multiple allowed origins, and is supported in all current browsers.

``` Content-Security-Policy: frame-ancestors 'none' ```

Equivalent to `X-Frame-Options: DENY`. No site, including your own, can frame the page.

``` Content-Security-Policy: frame-ancestors 'self' ```

Equivalent to `X-Frame-Options: SAMEORIGIN`.

``` Content-Security-Policy: frame-ancestors 'self' https://partner.example.com ```

Allows framing from your own origin and from a specific trusted partner — something `X-Frame-Options` cannot express.

Which to Use

Set both `X-Frame-Options: DENY` and `Content-Security-Policy: frame-ancestors 'none'` (or `'self'` if you need same-origin framing). The CSP directive takes precedence in browsers that support it; the `X-Frame-Options` header provides a fallback for older browsers.

How to Add These Headers

**Nginx:**

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

**Apache:**

```apache Header always set X-Frame-Options "DENY" Header always set Content-Security-Policy "frame-ancestors 'none'" ```

**Node.js / Express using Helmet:**

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

**Cloudflare Workers or Pages Headers file (`_headers`):**

``` /* X-Frame-Options: DENY Content-Security-Policy: frame-ancestors 'none' ```

**Vercel (`vercel.json`):**

```json { "headers": [ { "source": "/(.*)", "headers": [ { "key": "X-Frame-Options", "value": "DENY" }, { "key": "Content-Security-Policy", "value": "frame-ancestors 'none'" } ] } ] } ```

Verifying the Fix

After adding the headers, check them in your browser's developer tools: open the Network tab, click any request to your site, and look for `X-Frame-Options` and `Content-Security-Policy` in the Response Headers section.

You can also test framing directly by attempting to embed your site in an iframe on a local test page:

```html <iframe src="https://yoursite.com" width="800" height="600"></iframe> ```

If clickjacking protection is working, the browser will block the iframe and display an error in the console.

Important Caveats

**JavaScript frame-busting is not a reliable protection.** Many sites use JavaScript that checks whether the page is running inside a frame and breaks out if so. This approach can be defeated by attackers who add `sandbox` attributes to the iframe or use other techniques to disable JavaScript execution. Headers are the correct solution.

**The `frame-ancestors` directive in CSP does not protect against older browsers** (Internet Explorer does not support it). This is the reason to set both headers.

**If you use a CDN or reverse proxy**, ensure that the headers are being added at the layer that serves responses to the browser. Headers added at the application layer but stripped by the CDN will not reach the browser.

Clickjacking is one of several issues covered in the [complete guide to fixing security headers](/blog/how-to-fix-security-headers-uk) — if you're setting X-Frame-Options you're likely also missing Content-Security-Policy, HSTS, and a few others. It makes sense to fix them all in one pass. [What is a web application firewall](/blog/what-is-a-web-application-firewall-uk) is also worth reading if you want a layer that blocks attack attempts at the edge before they reach your application at all.

Yrzo AI's automated security assessment checks for `X-Frame-Options` and `Content-Security-Policy` frame-ancestors configuration on your website, along with 52 other security checks. Results in minutes at yrzoai.dev.

Find out if your website has these vulnerabilities

Yrzo AI runs 44 automated security checks and delivers a full report in under 20 minutes. Starting from £149.

Scan your website →