32

An answer to "Foolproof way to detect if iframe is cross domain" describes a method to test if an iframe on a page points to a same-domain or cross-domain page, working around different browsers' interpretations of the cross-domain policy and avoiding error messages that would interrupt a user or stop javascript.

I'm looking to do the equivalent of this but from the child page inside the iframe testing whether it is inside a cross-domain iframe or not.

It's possible to access (same domain) info about the parent with the parent global e.g. parent.document.location, but is there a reliable way to do this cross-browser without crashing into errors when it detects that the parent is cross-domain?


For handy testing, here's a jsbin inside a jsfiddle that crashes into an error when trying to test if parent.location.host is accessible. Is there a reliable way to get something usable like false telling us that this is a cross-domain parent, instead of an error?

Is there a try...catch variant which will be robust cross-browser? Or maybe some clever trick using a return value from parent.postMessage()? (although the parent page cannot be edited)


Use case: imagine embedable content that is to be embedded on pages on its own site, and by third parties. If the page is on a same-domain iframe, we hide branding and links back to the original site because the user is already here. If they access the page from a 3rd party iframe or load the page directly, we show the branding and credits so they can see the source of the content.


To clarify, I know about the same-domain policy and I don't care what the cross browser domain is, I'm just looking to write a simple but reliable if condition like if( crossDomainParent() ){ /* do something */}

Community
  • 1
  • 1
user56reinstatemonica8
  • 32,576
  • 21
  • 101
  • 125

8 Answers8

52

First check if you are IFramed.

window.self !== window.top

If you are IFramed, then your referrer is your parent frame url.

document.referrer

From this url you should be able to detect if you want to branch your code.

palanik
  • 3,601
  • 1
  • 13
  • 10
  • Interesting idea, +1. I've only done a little cross-browser testing so far, but based on that it does indeed look like you can safely check `document.referrer`, first checking it's not an empty string (which it will be if it's not framed in Firefox) then parsing it to get the domain. Can't test IE right now, will try later. Using http://jsfiddle.net/Ke3Z6/4/ and http://jsbin.com/loyuy/10/edit – user56reinstatemonica8 Feb 24 '14 at 10:13
  • 11
    document.referrer only works if the framed page has not navigated anywhere! As soon as this happens, referrer becomes previous framed page. – mikesjawnbit Apr 23 '14 at 20:09
  • Good point - luckily the framed content is content you have control over, so it may be possible to make sure the page doesn't navigate. – user56reinstatemonica8 Jan 10 '15 at 10:02
  • window.top throws the uncatchable security error in Chrome. – Craig Kovatch Oct 17 '16 at 21:56
  • 2
    The referrer is not a reliable value, it can be deactivated, forged, and in some cases not being sent by requirement, such as when the referrer is HTTPS and the target HTTP. It should work for your use case thought, but I wouldn't be too confident about it. One proper way to do this would be to manage 2 URL, one without branding but returning the HTTP header X-FrameOptions as SAMEORIGIN, the other with branding and without this header. – Maxime Rossini Feb 02 '17 at 08:46
  • working on a script for https://www.instacart.com/. None of the iframes have referers. – N-ate Oct 09 '17 at 01:02
  • 1
    The problem in chrome can be overcome by checking if parent === window. This of course would fail to be an accurate check in a popup. – N-ate Oct 09 '17 at 01:04
  • `referrer` is not reliable. We just had a bug because of this. If from inside iframe scope you're doing something like `location.reload()` it will send itself as the "referer" header to the backend and that's what will be in `document.referrer` after the iframe reloads. – Kaplan Ilya Jul 12 '22 at 13:19
11

Real cross-browser solution for posterity:

function isCrossOriginFrame() {
  try {
    return (!window.top.location.hostname);
  } catch (e) {
    return true;
  }
}

console.log(isCrossOriginFrame());

Tested on a decent swath of desktop and mobile browsers.

https://jsfiddle.net/arzd4btc/3/

Jeffrey Mixon
  • 12,846
  • 4
  • 32
  • 55
  • Thx @JeffMixon, I changed this slightly, to this: function isCrossOriginFrame() { try { return (document.location.hostname !== window.parent.location.hostname); } catch (e) { return true; } } – Jonathan Harris Feb 19 '19 at 14:50
3

I attempted to use referrer to determine cross-domain, but I discovered it was unreliable on many sites. Maybe someone will find this useful.

function IsCrossDomainFrame() {
    if( parent === window ) return false; //not a frame
    var parentLocation = new URL(document.referrer);//the referrer of an iframe is the parent
    return (parentLocation.protocol !== location.protocol ||
            parentLocation.hostname !== location.hostname ||
            parentLocation.port     !== location.port);
}

Protocol, hostname, and port determine cross-domain.

N-ate
  • 6,051
  • 2
  • 40
  • 48
  • 1
    please fix typo in "document.referer" it should be "document.referrer" (double r). Other than that this seems to be a pretty good solution from my testing so far. – strix25 Jan 17 '22 at 12:17
  • it would be good to add || parentLocation.href !== location.href because if crossdomain iframe is nested inside iframe without href check it will return that it is not crossdomain iframe – strix25 Oct 28 '22 at 10:07
  • @strix25, I don't understand how using href will improve anything. href contains the protocol (http(s)), hostname (domain.com), and port(80,443). href also includes elements that are not deterministic, e.g. query string, and possibly hash.. – N-ate Oct 29 '22 at 01:44
  • I had use case when crossdomain was nested inside of another iframe then port, protocol and hostname were the same. So the only difference then were HREF and PATH properties – strix25 Oct 31 '22 at 11:30
  • If the domains are the same, then it is not cross domain. What you're describing is a different page on the same domain. – N-ate Oct 31 '22 at 12:22
1

If I see your use case:

I would check it server side (who called your site), using $_SERVER['REMOTE_ADDR'], and if it is your IP than you can hide branding and links backs.

If the use case is about to prevent framing your site you can use X-Frame-Options: deny.

Other guess: Elements inside a document have a ownerDocument property, maybe that can help detecting what you want.

  • Interesting idea, but unfortunately I cannot change server side code in this project. I looked into the possibility of getting HTTP headers using javascript, and while [it is possible to get some HTTP header data client-side using `new XMLHttpRequest();`](http://stackoverflow.com/a/3215666/568458), it doesn't include data that is useful in this case. – user56reinstatemonica8 Feb 20 '14 at 14:21
1

With your current restrictions, there's no way to achieve this using the DOM API. Any evaluation with a window that belongs to another domain will spew out an error.

However, a "hack" would be to send an XHR from your child window to load a known page in your domain. If this XHR completes successfully, then you know that both windows are the same domain.

However, this will log an error message to the console.

ncabral
  • 2,512
  • 1
  • 19
  • 20
1

use x-frame header this will prevent to load your site to frame/iframe . there are various options read here

Pushker Yadav
  • 866
  • 1
  • 8
  • 15
  • 1
    This will make a browser display an error inside an iframe, instead of your website, i..e you'll lose iframed traffic altogether instead of jumping out of the iframe. – Benny K Aug 19 '20 at 02:37
1

Try this (in iframe)

<script type="text/javascript">
  var detectOrigin = (window.location.ancestorOrigins === undefined ? 
  /example.com/.test(document.domain) /* firefox */ : 
  /example.com/.test(window.location.ancestorOrigins[0])); /* webkit */
  if (detectOrigin === true) {console.log(detectOrigin)}; /* `true` example.com origin */
  if (detectOrigin === false) {console.log(detectOrigin)}; /* `false` !example.com origin */
</script>
guest271314
  • 1
  • 15
  • 104
  • 177
0

For nested iframes : a recursive way to know if iframe (where this code is executed) is cross domain or not :

function crosDomIfrm(win, data) {
        data = (typeof data === 'undefined')?{ref:win.document.location,isCD:false}:data;
        try { 
            if( win.document.referrer != '' ){
                data.isCD = parseURL(data.ref) != parseURL(win.document.referrer);
            }
        } catch(e){}
        if ( (win.self !== win.parent) && !data.isCD ) { //I'm in iframe
            data = crosDomIfrm( win.parent, data );
        }
        return {ref:data.ref,isCD:data.isCD};
    },

function parseURL(url) {
                var a=document.createElement('a');
                a.href=url;
                return a.hostname;
            }