21

What's the fastest way to check if my server is online via JavaScript?

I've tried the following AJAX:

function isonline() {
    var uri = 'MYURL'
    var xhr = new XMLHttpRequest();
    xhr.open("GET",uri,false);
    xhr.send(null);
    if(xhr.status == 200) {
        //is online
        return xhr.responseText;
    }
    else {
        //is offline
        return null;
    }   
}

The problem is, it never returns if the server is offline. How can I set a timeout so that if it isn't returning after a certain amount of time, I can assume it is offline?

Andrew Skirrow
  • 3,402
  • 18
  • 41
Skizit
  • 43,506
  • 91
  • 209
  • 269

7 Answers7

37

XMLHttpRequest does not work cross-domain. Instead, I'd load a tiny <img> that you expect to come back quickly and watch the onload event:

function checkServerStatus()
{
    setServerStatus("unknown");
    var img = document.body.appendChild(document.createElement("img"));
    img.onload = function()
    {
        setServerStatus("online");
    };
    img.onerror = function()
    {
        setServerStatus("offline");
    };
    img.src = "http://myserver.com/ping.gif";
}

Edit: Cleaning up my answer. An XMLHttpRequest solution is possible on the same domain, but if you just want to test to see if the server is online, the img load solution is simplest. There's no need to mess with timeouts. If you want to make the code look like it's synchronous, here's some syntactic sugar for you:

function ifServerOnline(ifOnline, ifOffline)
{
    var img = document.body.appendChild(document.createElement("img"));
    img.onload = function()
    {
        ifOnline && ifOnline.constructor == Function && ifOnline();
    };
    img.onerror = function()
    {
        ifOffline && ifOffline.constructor == Function && ifOffline();
    };
    img.src = "http://myserver.com/ping.gif";        
}

ifServerOnline(function()
{
    //  server online code here
},
function ()
{
    //  server offline code here
});
gilly3
  • 87,962
  • 25
  • 144
  • 176
  • 1
    Nicely found, but if I remember correctly you have to actually append it to the DOM, because not all browsers will load it if you don't. EDIT: You might want to use `onerror` because this will execute when the image doesn't exist: http://jsfiddle.net/8ZP9r/ – pimvdb Mar 07 '11 at 20:07
  • @pimvdb - Nice! Good call with the `onerror` suggestion! I'll update my answer. – gilly3 Mar 07 '11 at 20:12
  • Your AJAX suggestion doesn't appear to be working. Still hangs on offline. – Skizit Mar 07 '11 at 20:17
  • But what can I do, if I don't want to display the ping.gif in my document.body? – gurehbgui Jun 10 '14 at 11:45
  • 2
    @gurehbgui - Hide it with CSS. For example: `img.style.display = "none";` – gilly3 Jun 10 '14 at 19:22
  • 2
    Correct me if I'm wrong, but when we're sending a request to some other server, unless we know the address of an image in their server, it'll fail right..? and they can remove the image anytime... I don't understand what advantage using an image gives since we can send cross-domain XMLHttpRequest if we're in control of the servers. – T J Jan 01 '16 at 07:05
  • 1
    @gilly3, using `img` way can we have wrong result if requested image was cached by browser? – Sergey Novikov Jan 08 '17 at 16:40
  • @SergeyNovikov - Yes, potentially. The usual trick to avoid browser cache is to append a random or time-based querystring to the url. Eg, `img.src = "http://example.com/ping.gif?" + Date.now()`. – gilly3 Jan 09 '17 at 19:12
8

Here's how I achieved checking server availability using Fetch to manage the request and AbortController to handle a timeout within a Node.js application.

function checkServer(url, timeout) {
  const controller = new AbortController();
  const signal = controller.signal;
  const options = { mode: 'no-cors', signal };
  return fetch(url, options)
    .then(setTimeout(() => { controller.abort() }, timeout))
    .then(response => console.log('Check server response:', response.statusText))
    .catch(error => console.error('Check server error:', error.message));
}
Will P
  • 419
  • 5
  • 8
2

Add to gilly3 answer

In practice, I found the need to use document.body.appendChild quite slow.

Although the question is about pure JavaScript, using some HTML will make that solution much faster.

So I am leaving this code here for anyone looking for speed.

<html>
<head>
    <title>Some page</title>
</head>

<body>
  <img src="https://myserver.com/ping.gif"  onload="pageOnline()" onerror="pageOffline()">

  <script>


  function pageOnline() {
    // Online code
  }

  function pageOffline() {
    // Offline code
  }
</script>
</body>
</html>

Ng Sek Long
  • 4,233
  • 2
  • 31
  • 38
  • Simple solution, thank you. If server is down, it takes time to run pageOffline function. It takes 21sec on a google cloud server. – matasoy Feb 19 '21 at 09:40
  • 1
    You can setup another `setTimeout` function to redirect call the pageOffline for shorter amount of time (e.g. 5 seconds), then you will not be stuck for 21 seconds. That 21 seconds is actually the timeout time of a person browser, so not much we can change to help. – Ng Sek Long Dec 15 '21 at 02:21
0

Use an XMLHttpRequest, then check whether it failed. Not sure if this would work cross-domain though.

pimvdb
  • 151,816
  • 78
  • 307
  • 352
0

Make an ajax call and its result will tell.

d-live
  • 7,926
  • 3
  • 22
  • 16
  • I tried it, does not work: http://stackoverflow.com/questions/36007060/show-waiting-page-while-server-reboots – Black Mar 15 '16 at 13:12
0

The original answers are still valid, but quite outdated. Here is the approach I took:

import axios from 'axios'
import to from 'await-to-js'

export default async function pingAppReady(healthCheckPath: string, recursive = true) {
  return new Promise<void>(async (resolve, reject) => {
    // Ping the path e.g http://localhost:3000
    const [err] = await to(axios.get(healthCheckPath))

    // resolve if the ping returns no error or error that is not related to the connection
    if (!err) return resolve()
    if (err.code !== 'ECONNREFUSED') return resolve()

    if (!recursive) reject()

    setTimeout(async () => {
      await pingAppReady(healthCheckPath, recursive)
      resolve()
    }, 5000)
  })
}
Ivo
  • 364
  • 3
  • 7
0

Since XMLHttpRequest did not work for me for cors issues, I found and tried gilly3's answer and it works fine, if you call it once. It fails, when you check a server frequently, since the loaded graphics file is already in the cache of the browser. So I've added a random element as query string to avoid this. (A solution, I'v also found somewhere at stack overflow.)

function checkServer(callback, fqdn, imagepath="/favicon.ico") {
    let img = new Image();
    img.onload = function() { callback(fqdn, true); };
    img.onerror = function() { callback(fqdn, false); };
    img.src = "http://" + fqdn + imagepath + '?r=' + Math.random(); /* avoids caching */
}