6

I want to check if a server is reachable with a JavaScript function.

By reachable, I mean, if the server answers, I don't care which HTTP status code, it's reachable.

Stackoverflow/Google helped me to this function:

function check(target)
{
    var xhr = new XMLHttpRequest();
    var target = "https://"+target+"/index.php";
    var num = Math.round(Math.random() * 10000);

    xhr.open("HEAD", target + "?num=" + num, true);
    xhr.send();
    xhr.addEventListener("readystatechange", processRequest, false);

    function processRequest(e)
    {
      if (xhr.readyState == 4)
      {
        if (xhr.status !== 0)
        {
          return true;
        }
        else
        {
          return false;
        }
      }
    }
}

Which works just fine, if the target allows the action with Access-Control-Allow-Origin: * (or the client specifically). But that is not the case.

I've come across a number of solutions, which all seem to rely on that.

How can I check if a server is reachable, independent of the Access-Control-Allow-Origin settings on the server, with JavaScript?

edit: I just wanted to add, that I can not modify the target (e.g. change headers), but I'm able to identify resources that are supposed to be available (e.g. https://target/this-specific-site.php)

edit2: I'm trying to implement a solution, as suggested by @Vic; try it here:

function chk(){

var img = new Image();   // Create new image element

img.onload = function(){
    return false;
};

img.onerror = function() {
    return true;
};

// Try to change this URL to nonexistant image
img.src = 'https://www.google.com/images/srpr/logo3w.png'; // Set source path

}

if (chk())
{
alert("IMAGE LOADED");
}
else
{
alert("IMAGE FAILED TO LOAD ("+chk()+")");
}

but the function appears to return undefined, so the check fails.

edit3: I need the function to give me a return value.

SaAtomic
  • 619
  • 1
  • 12
  • 29
  • (1) Get an image from the website (2) Make it load in your website (3) If error occurs, assume the website is down. – Vic Mar 13 '17 at 07:48
  • @Quentin, well the pages do result in `0`, if you try your script with `https://enable-cors.org/` as a target, I think you'll see what I mean – SaAtomic Mar 13 '17 at 07:56
  • @Vic, I'm trying to implement this with the help of another Stackoverflow post. But for me both the function if it fails and if it loads are executed. See: https://jsbin.com/worutemobe/edit?html,console,output – SaAtomic Mar 13 '17 at 08:00
  • Just checked your code... Doesn't work like that, lol – Vic Mar 13 '17 at 08:21
  • Here you go, bruh https://jsbin.com/temuniwuzi/1/edit – Vic Mar 13 '17 at 08:23
  • Thanks, I had it working that far, but I need it to return `true` or `false`, depending on the outcome. – SaAtomic Mar 13 '17 at 08:26

4 Answers4

15

In modern browsers, you can use the fetch API and its no-cors request Mode which will return an opaque response (you won't be able to do anything else from it) :

fetch('http://google.com', {mode: 'no-cors'}).then(r=>{
  console.log('google is reachable');
  })
  .catch(e=>{
    console.log('google is not there');
    });
fetch('http://fakeservertahtdoesntexist.com', {mode: 'no-cors'}).then(r=>{
  console.log('fake is reachable');
  })
  .catch(e=>{
    console.log('fake is not there');
    });

Weirdly enough, my FF doesn't throw though (but it doesn't then either), while my chrome does throw.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • 1
    This looks quite promising! I've tried to implement it and I get `undefined`'s back. See https://jsbin.com/hadoluqire/edit?html,console,output – SaAtomic Mar 13 '17 at 09:04
  • 2
    So now you've got to understand [asynchronous scripting](http://stackoverflow.com/questions/6847697/how-to-return-value-from-an-asynchronous-callback-function) (any hack you'll use will be async anyway). – Kaiido Mar 13 '17 at 09:05
  • 1
    huh. this is becoming more complex than I thought. thanks though! – SaAtomic Mar 13 '17 at 09:11
  • 1
    Beware that fetch() is an experimental feature: "note that the syntax and behavior of an experimental technology is subject to change" (source: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) – Perspectivus Mar 13 '17 at 19:34
  • 3
    @Perspectivus fetch is in the Living Standards, I don't think it will drastically change anytime soon. Some methods has not been implemented byll UAs yet, but so goes for a lot of APIs (ehich eill also have this default MDN notice) – Kaiido Mar 13 '17 at 22:57
3

You could add a script tag with a src attribute referencing a file you know exists on the server and then using the onerror event to detect if it is not found:

<script src="nonexistent.js" onerror="alert('error!')"></script>

Credit to last comment of this answer.

You shouldn't run into cross origin issues using this solution.

Update

If you don't want to take the chance and run scripts you can use the img tag instead. Use the onerror event to detect failure and the onload event to detect success:

<html>
    <head>
        <img src="https://www.google.com/images/srpr/logo3w.pngx" onerror="failure()" onload="success()" height="0" width="0">
        <script>
            let _isSuccess = false;
            function success() {
                _isSuccess = true;
            }

            function failure() {
                _isSuccess = false;
            }

            function isSuccess() {
                console.log(`is success == ${_isSuccess}`);
                return _isSuccess;
            }

        </script>
    </head>
    <body onload="isSuccess()">
        <h1>Hello World!</h1>
    </body>
</html>
Community
  • 1
  • 1
Perspectivus
  • 972
  • 1
  • 9
  • 22
  • 2
    Wouldn't it be the same as loading external images? Loading up scripts is pretty scary. – Vic Mar 13 '17 at 08:39
  • 1
    this seems like a very similar option, but I don't see how I can use that in a function to give me `false` or `true`. – SaAtomic Mar 13 '17 at 08:43
  • 1
    @SaAtomic, to avoid asynchronous issues call the isSuccess() function in the onLoad() event of the tag – Perspectivus Mar 13 '17 at 09:33
2

Would this suffice? You can't have asynchronous functions (onerror/onload) return values. Instead, you can do something similar to callbacks.

function conditional(status){
  if(status)
  {
    console.log("Hey, the image loaded correctly.");
  }
  else
  {
    console.log("Bruh, the server is dead.");
  }
}

var tester = new Image();
tester.onload = function() {
  conditional(true);
};
tester.onerror = function() {
  conditional(false);
};
tester.src="https://i.imguhr.com/Ru1q3sS.jpg";

https://jsbin.com/veruzegohe/1/edit?html,console,output

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Vic
  • 703
  • 6
  • 9
  • This is neat. Appears to work just fine for me. Thanks for helping out a JS noob. – SaAtomic Mar 13 '17 at 08:47
  • Read up on callbacks, bruh. It will change your JS life. By the way, check if I answered your question. (Am I allowed to say that? Sry, SO noob) – Vic Mar 13 '17 at 08:51
  • I'm still a bit lost and try to implement this in my script. I don't get how I can use this as a function that returns true or false, so I can do `if(check()){[...];}` in another function. – SaAtomic Mar 13 '17 at 08:58
  • Bruh, you can't do returns asynchronously. IF statements require synchronous functions. I suggest putting your script inside the conditional function. – Vic Mar 13 '17 at 09:01
  • This is a great little hack to determine if a web server is up/reachable. – Fat Monk Apr 29 '20 at 12:43
  • I tried this and it seems like sometimes the browser serves the image from cache :-/ – AndreLung Jan 24 '21 at 18:58
1

This isn't possible. It would allow for information, that the same origin policy is designed to protect, to leak.

e.g. Does the company that the user works for, have an intranet page with a particular URL?

This could lead to such things as determining which version of Microsoft Sharepoint (to pick a well known example) the company uses, what URL it is installed on, and then using that information for a social engineering attack.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 1
    This must be achievable, as @Vic suggested, I can try to retrieve an image from the server and see if it loads. – SaAtomic Mar 13 '17 at 08:10
  • 1
    @SaAtomic you could also run in through proxy. Have PHP do the GET request. – Vic Mar 13 '17 at 08:19
  • 4
    Actually they did invent the `opaque` response and the `no-cors` request mode, which is supposed to be safe enough by allowing the request to be only HEAD, GET or POST. If I understand correctly, it avoids HEAD + GET or HEAD + POST but I'm not sure I get it right, and I'm not sure how it can avoid port sniffing either. – Kaiido Mar 13 '17 at 09:04