Catch error if iframe src fails to load . Error :-"Refused to display 'http://www.google.co.in/' in a frame.."

JqueryIframeknockout.jsSame Origin-Policy

Jquery Problem Overview


I am using Knockout.js to bind iframe src tag(This will be configurable with respect to User).

Now, if user has configured http://www.google.com (I know it won't load in iframe, thats why I am using it for -ve scenario) and that has to be shown in IFrame. but it throws error:-

> Refused to display 'http://www.google.co.in/'; in a frame because it > set 'X-Frame-Options' to 'SAMEORIGIN'.

I have the following code for Iframe:-

<iframe class="iframe" id="iframe" data-bind="attr: {src: externalAppUrl, height: iframeheight}">
    <p>Hi, This website does not supports IFrame</p>
</iframe>

What I want is, if the URL fails to load. I want to display Custom Message. Screenshot of console when I get error FIDDLE HERE

Now, if I use onload and onerror as:-

<iframe id="browse" style="width:100%;height:100%" onload="alert('Done')" onerror="alert('Failed')"></iframe>

It works fine loading w3schools.com but not with google.com.

Secondly:- if I make it as a function and try like I have done in my fiddle, it doesn't works.

<iframe id="browse" style="width:100%;height:100%" onload="load" onerror="error"></iframe>

I don't know how should I make it run and capture the error.

Edited:- I have seen Want to call a function if iframe doesn't load or load's question in stackoverflow but it shows error for sites that can be loaded in iframe.

Also, I have looked into Stackoverflow iframe on load event Thanks!!

Jquery Solutions


Solution 1 - Jquery

You wont be able to do this from the client side because of the Same Origin Policy set by the browsers. You wont be able to get much information from the iFrame other than basic properties like its width and height.

Also, google sets in its response header an 'X-Frame-Options' of SAMEORIGIN.

Even if you did an ajax call to google you wont be able to inspect the response because the browser enforcing Same Origin Policy.

So, the only option is to make the request from your server to see if you can display the site in your IFrame.

So, on your server.. your web app would make a request to www.google.com and then inspect the response to see if it has a header argument of X-Frame-Options. If it does exist then you know the IFrame will error.

Solution 2 - Jquery

I think that you can bind the load event of the iframe, the event fires when the iframe content is fully loaded.

At the same time you can start a setTimeout, if the iFrame is loaded clear the timeout alternatively let the timeout fire.

Code:

var iframeError;

function change() {
    var url = $("#addr").val();
    $("#browse").attr("src", url);
    iframeError = setTimeout(error, 5000);
}

function load(e) {
    alert(e);
}

function error() {
    alert('error');
}

$(document).ready(function () {
    $('#browse').on('load', (function () {
        load('ok');
        clearTimeout(iframeError);
    }));

});

Demo: http://jsfiddle.net/IrvinDominin/QXc6P/

Second problem

It is because you miss the parens in the inline function call; try change this:

<iframe id="browse" style="width:100%;height:100%" onload="load" onerror="error"></iframe>

into this:

<iframe id="browse" style="width:100%;height:100%" onload="load('Done func');" onerror="error('failed function');"></iframe>

Demo: http://jsfiddle.net/IrvinDominin/ALBXR/4/

Solution 3 - Jquery

The onload will always be trigger, i slove this problem use try catch block.It will throw an exception when you try to get the contentDocument.

iframe.onload = function(){
   var that = $(this)[0];
   try{
   		that.contentDocument;
   }
   catch(err){
   		//TODO 
   }
}

Solution 4 - Jquery

This is a slight modification to Edens answer - which for me in chrome didn't catch the error. Although you'll still get an error in the console: "Refused to display 'https://www.google.ca/'; in a frame because it set 'X-Frame-Options' to 'sameorigin'." At least this will catch the error message and then you can deal with it.

 <iframe id="myframe" src="https://google.ca"></iframe>

 <script>
 myframe.onload = function(){
 var that = document.getElementById('myframe');

 try{
    (that.contentWindow||that.contentDocument).location.href;
 }
 catch(err){
    //err:SecurityError: Blocked a frame with origin "http://*********" from accessing a cross-origin frame.
	console.log('err:'+err);
}
}
</script>

Solution 5 - Jquery

I solved it with window.length. But with this solution you can take current error (X-Frame or 404).

iframe.onload = event => {
   const isLoaded = event.target.contentWindow.window.length // 0 or 1
}

MSDN

Solution 6 - Jquery

Update: contentWindow.name will now always throw an error on cross-origin frames. Seems like the only way is to do this server side (for now). I have written a small cloudflare worker to capture headers for remote apis and it can be used here to check for X-Frame-Options.

Sample code to check before rendering in iframe: (jsfiddle: https://jsfiddle.net/2gud39aw/2/)

function checkUrlFrameOptions(apiurl){
  return fetch("https://header-inspector.repalash.workers.dev/?" + new URLSearchParams({
    'apiurl': apiurl,
    'headers': 'x-frame-options'
  }), {
    method: 'GET'
  }).then(r => r.json()).then(json => {
    let xFrameOp = (json.headers['x-frame-options'] || '').toLowerCase();
    // deny all requests
    if(xFrameOp==='deny') return false;
    // deny if different origin
    if(xFrameOp==='sameorigin' && json.origin !== location.origin) return false;
    return true;
  })
}

checkUrlFrameOptions("https://google.com").then((res)=>console.log("google.com can be loaded in iframe: ", res))
checkUrlFrameOptions("https://example.com").then((res)=>console.log("example.com can be loaded in iframe: ", res))

The cloudflare worker endpoint ( https://header-inspector.repalash.workers.dev ) is just for testing, don't use this in production. The code is available at: https://gist.github.com/repalash/b1e778dbe3ac2e7149831c530a6535f9 and can be deployed directly as a cloudflare worker

OLD Answer

Here's a simple solution, tested on Chrome and Safari.

    const iframe = document.createElement('iframe')
    iframe.onload = function() {
        try {
            iframe.contentWindow.name
        } catch (e) {
            if (e.message.includes('cross-origin')) console.warn(e.message);
            else console.error(e.message);
        }
    }

    iframe.src = "https://google.com";

jsFiddle demo: https://jsfiddle.net/k5e1mg3t/5/

Solution 7 - Jquery

I faced similar problem. I solved it without using onload handler.I was working on AngularJs project so i used $interval and $ timeout. U can also use setTimeout and setInterval.Here's the code:

 var stopPolling;
 var doIframePolling;
 $scope.showIframe = true;
 doIframePolling = $interval(function () {
    if(document.getElementById('UrlIframe') && document.getElementById('UrlIframe').contentDocument.head && document.getElementById('UrlIframe').contentDocument.head.innerHTML != ''){
        $interval.cancel(doIframePolling);
        doIframePolling = undefined;
        $timeout.cancel(stopPolling);
        stopPolling = undefined;
        $scope.showIframe = true;
    }
},400);

stopPolling = $timeout(function () {
        $interval.cancel(doIframePolling);
        doIframePolling = undefined;
        $timeout.cancel(stopPolling);
        stopPolling = undefined;
        $scope.showIframe = false;     
},5000);

 $scope.$on("$destroy",function() {
        $timeout.cancel(stopPolling);
        $interval.cancel(doIframePolling);
 });

Every 0.4 Seconds keep checking the head of iFrame Document. I somthing is present.Loading was not stopped by CORS as CORS error shows blank page. If nothing is present after 5 seconds there was some error (Cors policy) etc.. Show suitable message.Thanks. I hope it solves your problem.

Solution 8 - Jquery

I have the same problem, chrome can't fire onerror when iframe src load error.

but in my case, I just need make a cross domain http request, so I use script src/onload/onerror instead of iframe. it's working well.

Solution 9 - Jquery

As explained in the accepted answer, https://stackoverflow.com/a/18665488/4038790, you need to check via a server.

Because there's no reliable way to check this in the browser, I suggest you build yourself a quick server endpoint that you can use to check if any url is loadable via iframe. Once your server is up and running, just send a AJAX request to it to check any url by providing the url in the query string as url (or whatever your server desires). Here's the server code in NodeJs:

const express = require('express')
const app = express()

app.get('/checkCanLoadIframeUrl', (req, res) => {
  const request = require('request')
  const Q = require('q')

  return Q.Promise((resolve) => {
    const url = decodeURIComponent(req.query.url)

    const deafultTimeout = setTimeout(() => {
      // Default to false if no response after 10 seconds
      resolve(false)
    }, 10000)

    request({
        url,
        jar: true /** Maintain cookies through redirects */
      })
      .on('response', (remoteRes) => {
        const opts = (remoteRes.headers['x-frame-options'] || '').toLowerCase()
        resolve(!opts || (opts !== 'deny' && opts !== 'sameorigin'))
        clearTimeout(deafultTimeout)
      })
      .on('error', function() {
        resolve(false)
        clearTimeout(deafultTimeout)
      })
  }).then((result) => {
    return res.status(200).json(!!result)
  })
})

app.listen(process.env.PORT || 3100)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionShubhView Question on Stackoverflow
Solution 1 - JqueryEvan LarsenView Answer on Stackoverflow
Solution 2 - JqueryIrvin DomininView Answer on Stackoverflow
Solution 3 - JqueryH.EdenView Answer on Stackoverflow
Solution 4 - Jqueryuser2677034View Answer on Stackoverflow
Solution 5 - JqueryQeemercView Answer on Stackoverflow
Solution 6 - JqueryPalash BansalView Answer on Stackoverflow
Solution 7 - JqueryvireshView Answer on Stackoverflow
Solution 8 - JqueryEngin MorseView Answer on Stackoverflow
Solution 9 - Jquerylwdthe1View Answer on Stackoverflow