Health Check DNS Load Balanced Web Servers

by Eric Fortis

Consider example.com, which is load balanced with DNS using an ‘A’ record per server.

dig A example.com

example.com. 300 IN  A  192.0.2.111
example.com. 300 IN  A  192.0.2.222

Background

It’s uncommon to create TLS certificates with IPs as alternate names. So chances are that you‘ll need the actual domain name in order to fetch the page via HTTPS.

How to target a specific server?

Resolve the domain to a specific IP. Here are two ways of doing it, both timeout in two seconds.

curl

curl -so /dev/null --max-time 2 --head \
  --write-out "%{http_code}" \
  --resolve example.com:443:192.0.2.111 \
  https://example.com

Or, with Node.js

import https from 'https'

checkStatus('https://example.com', '192.0.2.111')
  .then(console.log)
  .catch(console.error)

function checkStatus(url, ip) {
  return new Promise((resolve, reject) => {
    const ipFam = 4
    const req = https.request(url,
      {
        method: 'HEAD',
        timeout: 2000,
        lookup(_, options, resolveDNS) {
           if (options.all) // Node 20+
             resolveDNS(null, [{ address: ip, family: ipFam }])
           else
             resolveDNS(null, ip, ipFam)
         }
      },
      response => { resolve(response.statusCode) }
    )
    req.on('error', reject)
    req.on('timeout', () => req.destroy())
    req.end()
  })
}

Open Source

This health-check.mjs shows how to send an email on failure.

Other use

Bypassing a reverse proxy

For example, if Cloudflare® is proxying a server, you can see the original response sent by the origin server (you’ll need to tweak the method and response output).

Avoided alternatives

/etc/hosts or DNS zone file

Avoid changing the /etc/hosts file because it’s system-wide, requires elevated privileges, and limits health checks to be sequential. Also, it depends on nsswitch.conf.

The same goes for changing a zone file of a caching DNS resolver like unbound.

Private Tunnels

Connecting via VPN or SSH wouldn’t be end-to-end health checks.

IP Address in the certificate’s SAN

Issuing certificates with an IP address in addition to the domain name it’s a needless expense if it’s only for health checking.

Skipping the hostname and certificate verification

For example, curl --insecure -L https://192.0.2.111