Detect whether or not a page is allowed to load within an iframe

<!DOCTYPE html>   
 <html>  
 <head>
  
<script type="text/javascript" src="content.js"></script>
 </head>
   <body>  
   <iframe id="iframe" src="e" style="width:500px; height: 600px;">
   </body>  
 </html>  


when I tested this I made src “e”
So it shouldnt be null

Hi,

Sorry, I only glanced at this thread in passing when I saw the problem with async/await. I’ve just read through it now and understand what you are trying to do.

The problem that you are going to run into doing this in JavaScript is CORS. CORS stands for Cross-Origin Resource Sharing and it’s a security feature implemented by web browsers to prevent a web page from making requests to a different domain than the one that served the web page. This helps to prevent malicious scripts from being able to access sensitive information or data from other domains.

Here is a page that will let you display it in an iframe: https://suricrasia.online/iceberg/

If we try to fetch this page using the FetchAPI and examine its headers, however, we get an error (note I am running this on a server):

const res = await fetch('https://suricrasia.online/iceberg/');
console.log(res);

// Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://suricrasia.online/iceberg/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
// Uncaught TypeError: NetworkError when attempting to fetch resource. 

What this means is that the response to the CORS request is missing the required Access-Control-Allow-Origin header, which is used to determine whether or not the resource can be accessed by content operating within the current origin (i.e. your script).

To get around this, we can use a proxy. This is an intermediary server that sits between a client and a server, allowing the client to bypass the same-origin policy and access resources from a different domain. This could be a server you own (you would then have to write the proxy), or (for demonstration purposes) we could use this one: https://corsproxy.io/

const res = await fetch('https://corsproxy.io/?' + encodeURIComponent('https://suricrasia.online/iceberg/'));
console.log(res);

Logs:

Response { 
  type: "cors", 
  url: "https://corsproxy.io/?https%3A%2F%2Fsuricrasia.online%2Ficeberg%2F", 
  redirected: false, 
  status: 200, ok: true, 
  statusText: "OK", 
  headers: Headers(4), 
  body: ReadableStream, 
  bodyUsed: false 
}

Success. If you inspect the headers property (an object itself), you will notice that there is no X-Frame-Options property.

Let’s try the same thing for a site that doesn’t let you embed it as an iframe:

const res = await fetch('https://corsproxy.io/?' + encodeURIComponent('https://google.com/'));
console.log(res);

// Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.google.com/sorry/index?continue=https://google.com/&q=EgSsR6BaGL3Ckp8GIindahRo9lxhrbiRVT8wPvK1empsgLvXpkKW7e5Mc2BKkQKzHQlNCqVt8TIBcg. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 429.

At this point, I think that this is a reasonable indication of whether the site will allow itself to be embedded as an iframe (please somebody correct me if I am wrong).

Which means we can do:

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="utf-8">
  <title>Document</title>
  <style>
    iframe {
      width:  500px;
      height:  600px;
    }
  </style>
</head>
<body>
  <script type="module">
    const link = 'https://suricrasia.online/iceberg/';
    // const link = 'https://google.com';

    try {
      const res = await fetch('https://corsproxy.io/?' + encodeURIComponent(link));
      const frame = document.createElement('iframe');
      frame.src = link;
      document.body.append(frame);
    } catch (e) {
      console.log(e);
      alert('iframe cannot be displayed');
    }
  </script>
</body>
</html>

Obviously, you’ll need to uncomment the link you want to test.


Although the above works for the handful of sites that I tested it on, I’m not sure this is the best solution. For example the https://corsproxy.io/ service could go away overnight. If you were after something more robust, I would consider looking into CORS further and creating a proxy yourself.

If you have access to a PHP environment, then this will allow you to inspect the values you need:

<pre>
  Wikipedia can be displayed in an iframe:
  <?php print_r(get_headers('https://www.wikipedia.org/', 1)); ?>

  suricrasia.online can be displayed in an iframe:
  <?php print_r(get_headers('https://suricrasia.online/iceberg/', 1)); ?>

  SitePoint cannot be displayed in an iframe:
  <?php print_r(get_headers('https://sitepoint.com/', 1)); ?>

  Google cannot be displayed in an iframe:
  <?php print_r(get_headers('https://google.com/', 1)); ?>
</pre>

As you can see when you run the script, an X-Frame-Options header is only present for the final two sites.

If we assume that this is always the case, the beginnings of a proxy could look like this:

<?php

function isDisplayableInIFrame($url) {
  $headers = get_headers($url, 1);
  return !isset($headers['X-Frame-Options']);
}

if (isset($_GET['url'])) {
  $url = $_GET['url'];
  $isDisplayableInIFrame = isDisplayableInIFrame($url);
  header('Content-Type: application/json');
  echo json_encode(['displayableInIFrame' => $isDisplayableInIFrame]);
} else {
  header('HTTP/1.1 400 Bad Request');
  echo 'ERROR: URL parameter missing in the request';
}

And you would call it like this:

const res = await fetch(`proxy.php?url=${encodeURIComponent('https://wikipedia.org')}`);
const json = await res.json();
console.log(json.displayableInIFrame);
// true

const res = await fetch(`proxy.php?url=${encodeURIComponent('https://google.com')}`);
const json = await res.json();
console.log(json.displayableInIFrame);
// false

This is a more robust solution and would give you an endpoint you control and allow you to inspect whatever headers you like.

HTH

Edit: Here’s a good link (kindly provided by @rpg_digital) on making a similar proxy in Node.

3 Likes

Hello, thanks for your answer.
What I am trying to do is embed the website. But when you are not logged in to the website it doesnt show anything so if the headers are the rejected ones I know the user is not logged in so I want the iframe to embed the login url.

Many thanks

Ok. So what is the address of the website you are trying to embed in the iframe?

Hello, thanks
But can I keep that private for now,
I will just use example.com and example.com/login

So if I understand things correctly, you are trying to do the following:

Did I get that right?

Thats correct, I am making this as a chrome extension so thats why I am using JS.

The way this normally works is that if you try to access a protected route on a site which you have to be logged in to view, the site will redirect you to a login page. Some sites (e.g. WordPress, GitHub etc) will also remember the page you were trying to access (passing the URL as a querystring parameter) and then once you have logged in, they will redirect you to where you wanted to go in the first place.

Which in your case, means that it should be sufficient to embed the URL you are trying to show in an iframe and have the site redirect you to the login if the user is not logged in.

If this doesn’t work, we will need another way to determine whether a user is logged in (maybe attempt to get a protected route and make a decision based on the response). I don’t really think that at this point it has anything to do whether the site can be displayed in an iframe.

Is the site which implements the login functionality and the protected route a site that you control?

How I know that the user is not logged in is when the headers are denied so I tried
If header is denied then redirect to login.
Else main page

I’m afraid its hard to say without knowing the site you are talking about.

If you want to DM me a link to the site in question, then I don’t mind taking a look.

So if I go on domain.com/dash for example without logging in the site automatically redirects you to domain.com/login. So the iframe should also redirect. But it shows domain.com refused to connect. My code before detects if headers then iframe src domain.com/dash. Else iframe src domain.com/login

Ok, well like I said, it’s kinda hard to say what the problem is without seeing the domain in question. Good luck in finding a solution :slight_smile:

The website is for an organisation so it is not possible for you to access it, but I can give you all the response headers and so on.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.