The danger of iframe clickjacking and how to deal with it


When developing an application, we need to ensure that our users are safe from various attacks. Unfortunately, the web has a lot of mechanisms that can be abused. In this article, we explore the idea of iframes and underline the danger of clickjacking. We also learn how to deal with this problem using the X-Frame-Options and Content-Security-Policy headers.

The Inline Frame element

With the element, we can embed an HTML document into the current page. The crucial thing is that the embedded page maintains a level of independence. This aims to provide a layer of security. Let’s look at the following example of a website hosted at


In the above code, we present a banking website in an iframe. We also embed the following code:


Above, as soon as the iframe loads, we find the button used for deleting an account and click it. Fortunately, it does not work.

Uncaught DOMException: Blocked a frame with origin “” from accessing a cross-origin frame.
at HTMLIFrameElement.<anonymous> (

The same-origin policy is the reason for the above error. We can’t access an iframe that embeds a website from another origin. It makes a lot of sense to block the attempts to tinker with the embedded website.


Unfortunately, the attackers found a clever way to work around the same-origin policy by using clickjacking. It involves tricking a user into clicking something different from what they think it is. Let’s create a straightforward example with it.


Above, we have both an iframe and a button. The user might intend to click the button, but it might be a trick. Let’s also look at the CSS that this website has.


The above CSS positions the iframe on top of the button. When the users click on the button, they click on the iframe instead. If the attackers position the iframe carefully, they might be able to trick the user into clicking the delete account button.

X-Frame-Options header

The most straightforward way of dealing with the above issue is disallowing other websites from embedding our page through iframes. To do that, we can use the X-Frame-Options response header.

The X-Frame-Options response header specifies whether we allow the browser to render our page in , , , and elements. It can have one of the three values:

    • by using the sameorigin value, we allow our page to be embedded by websites within the same origin.
    • with the deny value, we prevent all websites from embedding our page.

The X-Frame-Options header has a few shortcomings we need to know. Unfortunately, older versions of Firefox have a bug where would not work correctly in all cases.

Also, with X-Frame-Options, we can’t allow a particular website to embed our page. Therefore, the X-Frame-Options header might not fit our use case. An obsolete directive supports that, but we can’t rely on browsers to implement it. Therefore, if we want to allow just certain websites to embed our page, we can’t achieve that with X-Frame-Options.

Setting the X-Frame-Options header

If we use Node.js with Express to serve our content, setting the X-Frame-Options header is very straightforward.

We can also take advantage of the helmet library. For example, it can set the X-Frame-Options header for us, and much more.

If you want to know more about helmet, check out Increasing security of Express applications with the Helmet middleware

Content-Security-Policy header

In a recent article, we’ve covered Content-Security-Policy. It is a powerful header that allows us to control what resources the browser can load and execute.

One of the available directives is the . It allows us to specify a list of pages that can embed our website. There are a few values that we should cover.

    • works similarly to
    • works similarly to
    • allows a list of websites to embed our page
    • allows embedding our page both by websites within the same origin and an external trusted website

Content-Security-Policy vs. X-Frame-Options

By looking at the above, we can say that the directive is more flexible than the X-Frame-Options header.

According to, the browsers of roughly 93% of users support the directive. On the other hand, claims that more than 96% of users use browsers that support the X-Frame-Options header. Therefore, to be safe, we can set up both the Content-Security-Policy and the X-Frame-Options headers.

Intersection Observer v2

Another, more sophisticated way of dealing with clickjacking would be to use the Intersection Observer API. With it, we can observe the visibility and position of an element.

With Intersection Observer v2, we can also detect if an element covers another element or has some filters applied. We can use it to detect and block clickjacking attempts. Currently, only Chrome implements it, unfortunately. If you want to read more about it, check out this article by Thomas Steiner. Also, the Google Chrome Developers Youtube channel uploaded a video that explains it and provides a visual example.


In this article, we’ve gone through the idea behind clickjacking. We’ve also learned how to counter it with X-Frame-Options and Content-Security-Policy headers. While the latter is more flexible, it might still be worth using X-Frame-Options to cover more browsers. Thanks to the above, we’ve learned the danger clickjacking poses and how to deal with it.

Notify of
Inline Feedbacks
View all comments