diff --git a/bin/router b/bin/router
index 56db15569691be00b3498ca77ddffe1171575f22..dc409f51267387c05c08a9f0f6c540854cf6c25c 100755
--- a/bin/router
+++ b/bin/router
@@ -49,7 +49,10 @@ if (!config.get('browserid_url')) {
 
 // #1 - Setup health check / heartbeat middleware.
 // This is in front of logging on purpose.  see issue #537
-heartbeat.setup(app);
+var browserid_url = urlparse(config.get('browserid_url')).validate().normalize().originOnly();
+heartbeat.setup(app, {
+  dependencies: [browserid_url]
+});
 
 // #2 - logging!  all requests other than __heartbeat__ are logged
 app.use(express.logger({
@@ -119,7 +122,6 @@ wsapi.setup({
 }, app);
 
 // Forward all leftover requests to browserid
-var browserid_url = urlparse(config.get('browserid_url')).validate().normalize().originOnly();
 app.use(function(req, res, next) {
   forward(
     browserid_url+req.url, req, res,
diff --git a/lib/heartbeat.js b/lib/heartbeat.js
index 666e77767ac78f4fb8fdb74355a42a4bf870dadc..e5e309fd4c990366e7cb198bb549744e179ea550 100644
--- a/lib/heartbeat.js
+++ b/lib/heartbeat.js
@@ -4,20 +4,74 @@
 
 const
 urlparse = require('urlparse'),
-logger = require('./logging.js').logger;
+logger = require('./logging.js').logger,
+url = require('url');
 
 // the path that heartbeats live at
 exports.path = '/__heartbeat__';
 
+const checkTimeout = 5000;
+
 // a helper function to set up a heartbeat check
-exports.setup = function(app, cb) {
+exports.setup = function(app, options, cb) {
+  var dependencies = [];
+
+  if (typeof options == 'function') {
+    cb = options;
+  } else if (options && options.dependencies) {
+    dependencies = options.dependencies;
+  }
+  var count = dependencies.length;
+
   app.use(function(req, res, next) {
-    if (req.method === 'GET' && req.path === exports.path) {
-      function ok(yeah) {
-        res.writeHead(yeah ? 200 : 500);
-        res.write(yeah ? 'ok' : 'not ok');
-        res.end();
+    if (req.method !== 'GET' || req.path !== exports.path) {
+      return next();
+    }
+
+    var checked = 0;
+    var query = url.parse(req.url, true).query;
+    var deep = typeof query.deep != 'undefined';
+    var notOk = [];
+
+    // callback for checking a dependency
+    function checkCB (num) {
+      return function (err, isOk) {
+        checked++;
+        if (err) {
+          notOk.push(dependencies[num] + ': '+ err);
+        }
+
+        // if all dependencies have been checked
+        if (checked == count) {
+          if (notOk.length === 0) {
+            try {
+              if (cb) cb(ok);
+              else ok(true);
+            } catch(e) {
+              logger.error("Exception caught in heartbeat handler: " + e.toString());
+              ok(false, e);
+            }
+          } else {
+            logger.warn("heartbeat failed due to dependencies - " + notOk.join(', '));
+            ok(false, '\n' + notOk.join('\n') + '\n');
+          }
+        }
+      };
+    }
+
+    function ok(yeah, msg) {
+      res.writeHead(yeah ? 200 : 500);
+      res.write(yeah ? 'ok' : 'bad');
+      if (msg) res.write(msg);
+      res.end();
+    }
+
+    // check all dependencies if deep
+    if (deep && count) {
+      for (var i = 0; i < count; i++) {
+        check(dependencies[i] + exports.path, checkCB(i));
       }
+    } else {
       try {
         if (cb) cb(ok);
         else ok(true);
@@ -25,29 +79,39 @@ exports.setup = function(app, cb) {
         logger.error("Exception caught in heartbeat handler: " + e.toString());
         ok(false);
       }
-    } else {
-      return next();
     }
   });
 };
 
+
 // a function to check the heartbeat of a remote server
-exports.check = function(url, cb) {
+var check = exports.check = function(url, cb) {
   if (typeof url === 'string') url = urlparse(url).normalize().validate();
   else if (typeof url !== 'object') throw "url string or object required as argumnet to heartbeat.check";
   if (!url.port) url.port = (url.scheme === 'http') ? 80 : 443;
 
   var shortname = url.host + ':' + url.port;
 
-  require(url.scheme).get({
+  var timeoutHandle = setTimeout(function() {
+    req.abort();
+  }, checkTimeout);
+
+  var req = require(url.scheme).get({
     host: url.host,
     port: url.port,
     path: exports.path
   }, function (res) {
-    if (res.statusCode === 200) cb(true);
-    else logger.error("non-200 response from " + shortname + ".  fatal! (" + res.statusCode + ")");
-  }, function (e) {
-    logger.error("can't communicate with " + shortname + ".  fatal: " + e);
-    cb(false);
+    clearTimeout(timeoutHandle);
+    if (res.statusCode === 200) cb(null, true);
+    else {
+      logger.warn("heartbeat failure: non-200 response from " + shortname + ".  fatal! (" +
+                  res.statusCode + ")");
+      cb("response code " + res.statusCode);
+    }
+  });
+  req.on('error', function (e) {
+    clearTimeout(timeoutHandle);
+    logger.warn("heartbeat failure: can't communicate with " + shortname + ".  fatal: " + e);
+    cb(e ? e : "unknown error");
   });
 };
diff --git a/tests/heartbeat-test.js b/tests/heartbeat-test.js
new file mode 100755
index 0000000000000000000000000000000000000000..815f97120003b062da83fe9d898acc0fc16ff471
--- /dev/null
+++ b/tests/heartbeat-test.js
@@ -0,0 +1,122 @@
+#!/usr/bin/env node
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+require('./lib/test_env.js');
+
+const assert =
+require('assert'),
+vows = require('vows'),
+start_stop = require('./lib/start-stop.js'),
+wsapi = require('./lib/wsapi.js'),
+db = require('../lib/db.js'),
+config = require('../lib/configuration.js'),
+bcrypt = require('bcrypt'),
+http = require('http');
+
+var suite = vows.describe('heartbeat');
+
+// disable vows (often flakey?) async error behavior
+suite.options.error = false;
+
+start_stop.addStartupBatches(suite);
+
+// test deep and shallow heartbeats work for all processes
+[ 10004, 10002, 10003, 10004, 10007 ].forEach(function(port) {
+  [ true, false ].forEach(function(shallow) {
+    var testName = "shallow heartbeat check for 127.0.0.1:" + port;
+    suite.addBatch({
+      testName: {
+        topic: function() {
+          var self = this;
+
+          var req = http.get({
+            host: '127.0.0.1',
+            port: port,
+            path: '/__heartbeat__' + ( shallow ? "" : "?deep=true")
+          }, function(res) {
+            self.callback(null, res.statusCode);
+            req.abort();
+          }).on('error', function(e) {
+            self.callback(e, null);
+            req.abort();
+          });
+        },
+        "works":     function(err, code) {
+          assert.strictEqual(err, null);
+          assert.equal(code, 200);
+        }
+      }
+    });
+  });
+});
+
+// now let's SIGSTOP the browserid process and verify that the router's
+// deep heartbeat fails within 11s
+suite.addBatch({
+  "stopping the browserid process": {
+    topic: function() {
+      process.kill(parseInt(process.env['BROWSERID_PID'], 10), 'SIGSTOP');      
+      this.callback();
+    },
+    "then doing a deep __heartbeat__ on router": {
+      topic: function() {
+        var self = this;
+        var start = new Date();
+        var req = http.get({
+          host: '127.0.0.1',
+          port: 10002,
+          path: '/__heartbeat__?deep=true'
+        }, function(res) {
+          self.callback(null, res.statusCode, start);
+          req.abort();
+        }).on('error', function(e) {
+          self.callback(e, null);
+          req.abort();
+        });
+      },
+      "fails": function(e, code, start) {
+        assert.ok(!e);
+        assert.strictEqual(500, code);
+      },
+      "takes about 5s": function(e, code, start) {
+        assert.ok(!e);
+        var elapsedMS = new Date() - start;
+        assert.ok(3000 < elapsedMS < 7000);
+      },
+      "but upon SIGCONT": {
+        topic: function(e, code) {
+          process.kill(parseInt(process.env['BROWSERID_PID'], 10), 'SIGCONT');      
+          this.callback();
+        },
+        "a deep heartbeat": {
+          topic: function() {
+            var self = this;
+            var req = http.get(
+              { host: '127.0.0.1', port: 10002, path: '/__heartbeat__?deep=true'},
+              function(res) {
+                self.callback(null, res.statusCode);
+                req.abort();
+              }).on('error', function(e) {
+                self.callback(e, null);
+                req.abort();
+              });
+          },
+          "works": function(err, code) {
+            assert.ok(!err);
+            assert.strictEqual(200, code);
+          }
+        }
+      }
+    }
+  }
+});
+
+
+start_stop.addShutdownBatches(suite);
+
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);
diff --git a/tests/lib/start-stop.js b/tests/lib/start-stop.js
index b73aa2ea8f4951a1c615788e750259ab93bd7ece..742fb032745d0607052ff7b19de83157dff95e8a 100644
--- a/tests/lib/start-stop.js
+++ b/tests/lib/start-stop.js
@@ -46,10 +46,13 @@ function setupProc(proc) {
         }
       }
       var tokenRegex = new RegExp('token=([A-Za-z0-9]+)$', 'm');
+      var pidRegex = new RegExp('^spawned (\\w+) \\(.*\\) with pid ([0-9]+)$');
 
       if (!sentReady && /^router.*127\.0\.0\.1:10002$/.test(x)) {
         exports.browserid.emit('ready');
         sentReady = true;
+      } else if (!sentReady && (m = pidRegex.exec(x))) {
+        process.env[m[1].toUpperCase() + "_PID"] = m[2]; 
       } else if (m = tokenRegex.exec(x)) {
         if (!(/forwarding request:/.test(x))) {
           tokenStack.push(m[1]);