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.