Identifying Cross-Origin (CORS) Error from Other Errors in JavaScript's XMLHttpRequest()

I am currently working on detecting when an XMLHttpRequest() fails due to a Cross Origin Error rather than a bad request. Take, for example:

    ajaxObj=new XMLHttpRequest()
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);

Let's consider four scenarios regarding the URL:

Scenario 1: The URL is a valid address with the proper access-control-allow-origin set up.

  • Example: http://192.168.8.35 where the server has Access-Control-Allow-Origin: * in the header
  • This situation can be easily identified by checking if ajaxObj.readyState==4 and ajaxObj.status==200

Scenario 2: The URL is an invalid address on an existing server.

  • Example: http://xyz.google.com where the server responds but the request is invalid
  • In this case, ajaxObj.readyState==4 and ajaxObj.status==0

Scenario 3: The URL points to a non-existing server IP address.

  • Example: http://192.168.8.6 on a local network where there is no response
  • This results in ajaxObj.readyState==4 and ajaxObj.status==0

Scenario 4: The URL is a valid address where access-control-allow-origin is NOT set

  • Example: http://192.168.8.247 with a server that does not have Access-Control-Allow-Origin: * set in the header
  • This leads to ajaxObj.readyState==4 and ajaxObj.status==0

The issue at hand is: How can one differentiate between Case 4 (access-control-allow-origin error) and Cases 2 & 3?

For Case 4, Chrome's debug console displays the following error:

XMLHttpRequest cannot load http://192.168.8.247/. Origin http://localhost is not allowed by Access-Control-Allow-Origin.

How can this error be flagged in Javascript?

I attempted to find some indication within the ajaxObj object, but nothing seems to distinguish it from Cases 2 & 3.

Here is a simple test I conducted:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CORS Test</title>
<script type="text/javascript">
function PgBoot()
{
//  doCORS("http://192.168.8.35");   // Case 1
//  doCORS("http://xyz.google.com"); // Case 2
    doCORS("http://192.168.8.6");    // Case 3
//  doCORS("http://192.168.8.247");  // Case 4
}

function doCORS(url)
{
    document.getElementById("statusDiv").innerHTML+="Processing url="+url+"<br>";
    var ajaxObj=new XMLHttpRequest();
    ajaxObj.overrideMimeType('text/xml');
    ajaxObj.onreadystatechange = function()
    {
        var stat=document.getElementById("statusDiv");
        stat.innerHTML+="readyState="+ajaxObj.readyState;
        if(ajaxObj.readyState==4)
            stat.innerHTML+=", status="+ajaxObj.status;
        stat.innerHTML+="<br>";
    }
    ajaxObj.open("GET", url, true); 
    ajaxObj.send(null);
}
</script>
</head>
<body onload="PgBoot()">
<div id="statusDiv"></div>
</body>
</html>

Results using Chrome:

Processing url=http://192.168.8.35
readyState=1
readyState=2
readyState=3
readyState=4, status=200

Processing url=http://xyz.google.com
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.6
readyState=1
readyState=4, status=0

Processing url=http://192.168.8.247
readyState=1
readyState=4, status=0

Answer №1

According to the guidelines set by the W3C Spec, there is no method available to differentiate between a failed network connection and a failed CORS exchange.

The CORS specification outlines the procedure for a simple cross-origin request, emphasizing steps to be followed during the request process:

Execute the necessary steps for making a request while adhering to the specified rules.

In scenarios where the manual redirect flag remains unset and a response with HTTP status codes 301, 302, 303, 307, or 308 is received: Implement the redirect actions.

If the request is canceled by the end user: Initiate the abort protocol.

In cases of network errors: In situations involving DNS issues, TLS negotiation failures, or other network-related errors, follow the prescribed network error response. Avoid any form of end user interaction...

In all other instances: Conduct a comprehensive resource sharing assessment. If unsuccessful, resort to the network error procedures...

In instances of either a futile network connection or an unsuccessful CORS interchange, the guidelines detailed in the network error steps are executed, leading to an indistinguishable outcome between the two scenarios.

Why is this approach essential? It serves as a protective measure against potential threats seeking to analyze the LAN's network topology. For instance, a malevolent script on a webpage could extract your router's IP address through an HTTP interface request, gaining insights into your network configuration (such as the size of your private IP block - whether it is a /8 or a /16). As routers typically do not transmit CORS headers, this strategy ensures that the script acquires no sensitive details whatsoever.

Answer №2

Here's an alternative method that may assist some individuals. It involves a workaround for dealing with the difference between CORS and network errors, specifically in Chrome or Firefox (although it's not a foolproof solution).

var external = 'your url';

if (window.fetch) {
    // This code block is for browsers like Chrome or Firefox which support native fetch
    fetch(external, {'mode':'no-cors'})
        .then(function () {
            // The external URL is reachable but fails due to CORS restrictions
            // Fetch will still trigger if it's a CORS error
        })
        .catch(function () {
            // The external URL is not reachable
        });
} else {
    // For non-updated Safari or older versions of IE...
    // Error type detection in this case is unknown
}

Answer №3

To distinguish a CORS violation from other failed AJAX requests, you can examine the response headers of a HEAD request using server-side code and send back the findings to your client page. For instance, if the AJAX request fails (status 0), you could utilize this script (let's name it cors.php) to check if the response headers contain Access-Control-* headers.

Examples:

cors.php?url=http://ip.jsontest.com
cors.php?url=http://www.google.com
cors.php?url=http://10.0.0.1

result

HTTP/1.1 200 OK Access-Control-Allow-Origin: *
HTTP/1.1 302 Found
Invalid request


cors.php - Modify as necessary

<?php /* cors.php */
$url = $_GET["url"];
if(isset($url)) {
    $headers = getHeaders($url);
    header("Access-Control-Allow-Origin: *");

    if(count($headers) == 0) {
        die("Invalid request"); // No headers returned on bad URLs
    } else {
        echo $headers[0];       // Output the HTTP status code
    }

    // Include any CORS headers
    foreach($headers as $header) {
        if(strpos($header, "Access-Control") !== false) {
            echo " " . $header;
        }
    }
}

function getHeaders($url, $needle = false) {
    $headers = array();
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4);        // Timeout in seconds
    curl_setopt($ch, CURLOPT_TIMEOUT, 4);               // Timeout in seconds
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_VERBOSE, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');    // HEAD request only
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use(&$headers) {
        array_push($headers, $header);
        return strlen($header);
    });
    curl_exec($ch);
    return $headers;
} /* Drakes, 2015 */

Client-side test harness:

function testCORS(url, $elem) {
    $.ajax({
      url: url,
      timeout: 4000
    })
    .fail(function(jqXHR, textStatus) {
       if(jqXHR.status === 0) {
           // Check for CORS violation
           $.ajax({
              context: url,
              url: "http://myserver.com/cors.php?url=" + escape(this.url),
           })
           .done(function(msg) {
              if(msg.indexOf("HTTP") < 0) {
                $elem.text(url + " - doesn't exist or timed out");
              } else if(msg.indexOf("Access-Control-Allow-Origin") >= 0) {
                $elem.text(url + " - CORS violation because '" + msg + "'");
              } else {
                $elem.text(url + " - no Access-Control-Allow-Origin header set");
              }
           });
       } else {
           // Some other failure (e.g. 404), not related to CORS
           $elem.text(url + " - failed because '" + responseText + "'");
       }
    })
    .done(function(msg) {
      // Successful ajax request
      $elem.text(this.url + " - OK");
    }); /* Drakes, 2015 */
}

Harness driver:

// Create a div and append the results of the URL calls
$div = $("<div>");
$("body").append($div);

var urls = ["http://ip.jsontest.com", "http://google.com", "http://10.0.0.1"];
urls.map( function(url) {
   testCORS(url, $div.append("<h4>").children().last());
});

The outcomes:

http://ip.jsontest.com - OK

http://google.com - no Access-Control-Allow-Origin header set

http://10.0.0.1 - doesn't exist or timed out

Answer №4

Interestingly, there is a way to achieve this for images (and perhaps a similar solution exists for other specific content types). It seems that <img> tags do not abide by CORS rules, leading to the scenario where the statement "url fails to load by XHR && url succeeds to load by img src" suggests that the URL is actually working but being blocked by CORS restrictions.

While this method may not be effective in all situations, it can be useful in certain cases (such as confirming proper CORS configuration). For instance, I needed to trigger a warning in the console if my app (which has separate frontend and backend components) was installed with the backend URL correctly configured but had improper CORS settings on the server. In this scenario, using an image served as the perfect test.

function testCors(url) {
    var myRequest = new XMLHttpRequest();
    myRequest.open('GET', url, true);
    myRequest.onreadystatechange = () => {
        if (myRequest.readyState !== 4) {
            return;
        }
        if (myRequest.status === 200) {
            console.log(url, 'ok');
        } else {
            var myImage = document.createElement('img');
            myImage.onerror = (...args) => {console.log(url, 'other type of error', args);}
            myImage.onload = () => {console.log(url, 'image exists but cors blocked');}
            myImage.src = url;
            console.log(url, 'Image not found');
        }
    };
    myRequest.send();
}

Answer №5

In order to address the issue at hand, I devised a solution that requires modifications on the remote server by adding a new page.

  1. To begin, I created a probe.html page designed to be displayed within an iframe on the remote server where CORS communication is needed.
  2. Next, I implemented a window.onmessage event handler in my own page.
  3. I then loaded the probe.html page onto my page using a secure iframe element.
  4. Upon the iframe's onload event in my page, I transmitted a message to the iframe utilizing the window.postMessage() method.
  5. The probe.html page conducts checks on the URL from the same origin within the iframe and relays back the xhr.status along with xhr.statusText response details using window.postMessage().
  6. If an error status is detected, it logs the status and statusText information to our logging service for further analysis.

It is important to note that ensuring security can be challenging when passing parameters bidirectionally (as anyone can embed your iframe or manipulate postMessage events).

While this approach may not be foolproof and is limited in detecting transient errors, it does provide valuable insights into error causes, particularly useful in scenarios where direct access to users' browsers is not feasible.

Moreover, contrasting the suggested PHP solution wherein servers communicate directly, my method offers a more targeted approach to diagnosing communication failures without exposing the server to potential vulnerabilities through PHP's curl function.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Attempting to transpile JavaScript or TypeScript files for compatibility within a Node environment

Our node environment requires that our JavaScript files undergo Babel processing. Figuring out how to handle this has been manageable. The challenge lies in the fact that we have a mix of file types including .js, .jsx, .ts, and .tsx, which is not subject ...

Activate the mouseenter event as soon as an animated element makes contact with the cursor

As I venture into the world of web development, I am currently working on creating a game where the main objective is to avoid touching moving circles with the cursor. My approach involved using a mouseenter event as shown in the JSFiddle example below: $ ...

A guide on personalizing HTML for Django forms

I have implemented an HTML template on my website and need to capture information for my Django backend. Specifically, I am trying to extract the email address from this section of code: <input type="text" placeholder="Email" value="" style="height: 30 ...

Issue with ASP.NET Core Controller not properly receiving complete JavaScript array object from Ajax call

When passing a JavaScript Object Array through ajax to the controller, I noticed that if there are more than 11 objects in the array, the controller receives it as null. However, with 11 or fewer objects, the array is received successfully. Here is an exam ...

Choose a specific 24-hour range with the Date Interval Selector

I am currently utilizing the Date Range Picker plugin for bootstrap from the website http://www.daterangepicker.com/#examples, and I have a requirement to set the maximum date time range to be within 24 hours. Below is an example demonstrating how I can s ...

What is the method employed by Node.js to manage relative paths?

I am facing an issue with how Node.js handles paths. Despite checking the documentation, I couldn't find the solution to my problem. Basically, I have a file that contains a relative path pointing to another file (specifically a PNG image). Dependin ...

Having trouble with jQuery validation: Seeking clarification on the error

I have implemented some validations on a basic login page and added jQuery validation upon button click. However, the code is not functioning as expected. I have checked the console for errors but none were displayed. Here is the code for your review: ...

The array functions properly when handwritten, but fails to work when loaded from a text file

I have been developing a password recommendation script that aims to notify users when they are using a commonly used password. In order to achieve this, I decided to load the list of common passwords from an external text file. However, it seems that the ...

Display the X button solely once text has been inputted into the textbox

I have integrated a text clearing plugin from here in my project to allow users to clear the input field by clicking on an X button. However, I want to modify the code so that the X button only appears when there is text entered in the textbox. I attempt ...

Adding an element to a blank array using Angular

When attempting to add a new item to an empty array, I encounter an error: $scope.array.push is not a function. However, when the array is not empty, the push operation works flawlessly and updates my scope correctly. Just so you know, my array is initia ...

Trigger a pop-up alert box when the jQuery event $(document).ready is fired within a Smarty template

I'm currently attempting to make a popup message display when the document is fully loaded. Although I have successfully integrated Google Maps on another page, this task seems to be more challenging. Below is the code snippet: <html> < ...

Create a directive for AngularJS that utilizes SVG elements without using the deprecated

I rely heavily on directives for creating and manipulating intricate SVGs. With the deprecation of "replace" in directive factories starting from version 1.3.??, I am facing a dilemma on how to construct a valid SVG without utilizing replace: true in my di ...

Choose multiple children and grandchildren by checking the root checkbox multiple times

jquery/javascript $('.ic_parent').change(function(){ var id = $(this).attr('value') while (id.length >0){ if ($(this).attr('checked')) { $('#update_ics_table').find('input:checkbox[id = "child- ...

Create a set of n randomly generated points, represented as latitude and longitude pairs, originating from the central point of a polygon while remaining within the boundaries

I am currently trying to plot data within a polygon (District), but unfortunately, I do not have specific coordinates for each of the data sources in that district. My goal is to accurately display all the results from a district within its boundaries, wh ...

Patience is key when letting AJAX calls complete their execution in jQuery functions

In each of my 3 functions, I have a synchronous AJAX call. Here is an example: function() { a(); b(); c(); } a() { ajaxGet(globals.servicePath + '/Demo.svc/GetDemoList/' + sessionStorage.SessionId,function(data, success) {}, '&apos ...

Utilizing jQuery.ajax() to retrieve the child div from a separate page

Can anyone help me figure out how to use jQuery ajax() to load two children contents from an external page? I want a pre-loader to display before the main content loads. Below is the code snippet that I have tried. $.ajax({ url: 'notification.htm ...

Issue with jQuery and AJAX on the webpage

I've recently implemented the search functionality using AJAX jQuery, but I am facing a problem. Currently, when I type something in the search field, the search list displays accordingly. However, upon refreshing the page, the search results disappea ...

Creating a monorepo project in JavaScript that mimics the structure of Visual Studio projects

When working on a typical .NET Core project (with Visual Studio 2019), the structure typically looks like this: Example/ |-- Example.Common/ | |-- FileExample1.cs | |-- FileExample2.cs | `-- Example.Common.csproj |-- Example.WebAPI/ | |-- Control ...

The functionality of Ajax seems to be impaired when embedded within a JavaScript page in the Laravel framework

Hey everyone, I've encountered an issue while trying to split this code into a separate JavaScript file from the Laravel page. In the Laravel page: $(".importer").click(function(e) { // Prevents the form from reloading ...

Decoding arrays of JSON data

Receiving "chunked" data (json arrays) on the front-end via "xhr" (onprogress). Handling delayed chunks is easy - just remember response length and offset. The challenge comes when multiple "chunked" responses arrive simultaneously, resulting in an unpars ...