October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
AbortSignal

How to Build a Website Monitoring Script in Node.js

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A reliable Node.js website check does four things on every run: sends a request to a configured URL, verifies the returned HTTP status (and optionally a content marker), measures elapsed time, and fails explicitly when the request exceeds a deadline or raises a network error. The script below uses Node’s built-in fetch and AbortSignal.timeout(), then repeats at a controlled interval without allowing overlapping checks.

What this monitor actually checks

This is a small availability checker, not a complete synthetic-monitoring platform. It can tell you whether an endpoint responded with an allowed status, how long that response took, and whether DNS, TLS, connection, or timeout errors occurred. Alert delivery, durable history, retries, and deployment are separate decisions that depend on your environment.

  • HTTP health: a fulfilled fetch promise only means that response headers arrived. A 404 or 500 still fulfills the promise, so inspect response.status or response.ok.
  • Latency: record elapsed milliseconds for successful and failed attempts; a slow 200 response is different from an immediate connection refusal.
  • Deadline: abort requests that exceed a finite limit and count the abort as a failed check.
  • Content (optional): for a page that must contain a stable phrase, read the body and test that marker in addition to the status.

Prerequisites and configuration

Use a current Node.js release with the built-in Fetch API. AbortSignal.timeout(delay) was added in Node.js 17.3.0 and 16.14.0; verify the runtime selected for production before relying on it. The example is written as an ES module.

  1. Create a directory and run npm init -y.
  2. Add "type": "module" to package.json, or save the file with an .mjs extension.
  3. Set the target and timing values as environment variables rather than embedding a production URL.
MONITOR_URL=https://example.com 
MONITOR_INTERVAL_MS=60000 
MONITOR_TIMEOUT_MS=10000 
MONITOR_EXPECTED_STATUS=200 
node monitor.mjs

On Windows PowerShell, set variables with $env:MONITOR_URL="https://example.com" and run node monitor.mjs. Validate that the URL uses http: or https:; reject credentials or unexpected schemes if untrusted users can edit configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Dell PowerEdge T340 Tower Server, Windows 2019 STD OS, Intel Xeon E-2124 Quad-Core 3.3GHz 8MB, 32GB DDR4 RAM, 8TB Storage, RAID, Single PSU (Renewed)
  • 3.5 Inch Hot Plug Hard Drive PowerEdge T340 Tower Server Chassis
  • Microsoft Windows Server 2019 Standard Operating System
  • Processors: Intel Xeon E-2124 Quad-Core 3.3GHz 8MB CPU, Up To 4.3GHz Turbo
  • Memory: 32GB (2 x 16GB) DDR4 PC4-21300 2666MHz Unbuffered Memory
  • Hard Drive: 8TB (4 x 2TB) 7.2K RPM 6Gb/s SATA 3.5 Inch HDDs in RAID

Complete Node.js monitoring script

Save this as monitor.mjs. It performs one check immediately, then schedules the next run only after the previous one finishes, preventing accidental overlap when a site is slow.

const target = process.env.MONITOR_URL;
const intervalMs = Number(process.env.MONITOR_INTERVAL_MS ?? 60_000);
const timeoutMs = Number(process.env.MONITOR_TIMEOUT_MS ?? 10_000);
const expectedStatuses = new Set(
  (process.env.MONITOR_EXPECTED_STATUS ?? "200")
    .split(",")
    .map(value => Number(value.trim()))
    .filter(Number.isInteger)
);
const expectedText = process.env.MONITOR_EXPECTED_TEXT ?? "";

if (!target) throw new Error("MONITOR_URL is required");
let parsed;
try {
  parsed = new URL(target);
  if (!["http:", "https:"].includes(parsed.protocol)) throw new Error("unsupported protocol");
} catch (error) {
  throw new Error(`Invalid MONITOR_URL: ${error.message}`);
}
if (!Number.isFinite(intervalMs) || intervalMs < 1) throw new Error("MONITOR_INTERVAL_MS must be positive");
if (!Number.isFinite(timeoutMs) || timeoutMs < 1) throw new Error("MONITOR_TIMEOUT_MS must be positive");
if (expectedStatuses.size === 0) throw new Error("MONITOR_EXPECTED_STATUS must contain a status code");

let stopping = false;

async function checkOnce() {
  const started = performance.now();
  let response;
  try {
    const signal = AbortSignal.timeout(timeoutMs);
    response = await fetch(parsed, {
      method: "GET",
      signal,
      redirect: "follow",
      headers: { "user-agent": "basic-node-monitor/1.0" }
    });

    const elapsedMs = Math.round(performance.now() - started);
    let contentOk = true;
    if (expectedText) {
      const body = await response.text();
      contentOk = body.includes(expectedText);
    }

    const statusOk = expectedStatuses.has(response.status);
    const ok = statusOk && contentOk;
    const result = {
      time: new Date().toISOString(),
      ok,
      status: response.status,
      elapsedMs,
      contentOk: expectedText ? contentOk : undefined
    };
    console.log(JSON.stringify(result));
    return ok;
  } catch (error) {
    const elapsedMs = Math.round(performance.now() - started);
    const aborted = error?.name === "TimeoutError" || error?.name === "AbortError";
    console.error(JSON.stringify({
      time: new Date().toISOString(),
      ok: false,
      elapsedMs,
      error: aborted ? "request_timeout" : error?.name ?? "Error",
      message: String(error?.message ?? error)
    }));
    return false;
  }
}

async function run() {
  await checkOnce();
  if (!stopping) setTimeout(run, intervalMs);
}

function stop(signal) {
  stopping = true;
  console.log(JSON.stringify({ time: new Date().toISOString(), event: "stopping", signal }));
}
process.on("SIGINT", () => stop("SIGINT"));
process.on("SIGTERM", () => stop("SIGTERM"));

run().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

Set MONITOR_EXPECTED_STATUS=200,204 when an endpoint legitimately returns either code. A redirect is followed by default; if redirects themselves matter, set redirect: "manual" and define the acceptable 3xx statuses explicitly. The optional MONITOR_EXPECTED_TEXT check downloads the body, so use it only for pages with a stable, non-personalized marker.

Why each part matters

Status is a separate decision from transport success

Fetch resolves when it receives a response, including HTTP errors. The script therefore compares the response status with a configured allow-list. This avoids reporting an error page as healthy merely because the server answered.

Elapsed time identifies degradation

performance.now() measures the duration of each attempt. Logging it for failures as well as successes helps distinguish a fast refusal, a slow upstream, and a timeout. It is a measurement for this process, not a browser-rendering metric.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Dell PowerEdge R730xd Server 24B SFF 2U, 2X Intel Xeon E5-2690 v4 2.6Ghz (28-cores Total), 128GB DDR4 RAM, 4X 1.2TB 10K SAS 2.5” 12Gb/s HDD, H730P 2GB RAID, NIC 10Gb + I350 1Gb (Renewed)
  • Dell PowerEdge R730xd 24B SFF 2U Server
  • 2x Intel Xeon E5-2690 v4 2.6Ghz 14-Core (28-cores Total)
  • 128GB DDR4 RAM – 4x 1.2TB 10K SAS 2.5” 12Gb/s
  • Dell H730P mini 2GB 12Gb/s RAID
  • 2x 750W PSU - 2x 10Gb SFP+ 2x 1Gb (RJ45) NIC

AbortSignal enforces the deadline

AbortSignal.timeout(timeoutMs) creates a signal that aborts after the selected number of milliseconds. The catch block turns both timeout forms used by Node (TimeoutError or AbortError, depending on runtime) into a failed check. No response status is treated as success.

Content checks catch soft failures

A site can return status 200 while serving a maintenance page or an application error. Set an expected marker only when it is stable. Avoid matching volatile timestamps, user names, or localized text.

Scheduling, retries, and history

The recursive setTimeout starts the next run after the current run completes. That prevents two requests from being in flight at once. A fixed interval measured from wall-clock start would be simpler, but it can create overlap when a request lasts longer than the interval.

Do not turn every transient error into an alert. A common policy is to record every result, retry a failed attempt once with a short backoff, and alert only after consecutive failures. The appropriate interval, retry count, and alert destination are context-specific; keep them configurable rather than presenting one universal value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Hewlett Packard Enterprise ProLiant MicroServer Gen11 Tower Server, Intel Pentium Gold G7400 Processor, 16GB Memory, 1TB HDD Storage, External 180W US Power Supply (HPE Smart Choice P74439-005)
  • MODEL P74439-005: Compact and affordable HPE ProLiant MicroServer Gen11 powered by Intel Pentium Gold G7400 3.7GHz processor, ideal for file sharing, NAS, and basic business workloads
  • READY OUT OF THE BOX: Includes 16GB DDR5 UDIMM memory (expandable to 128GB), one 1TB SATA 6G Business Critical HDD, embedded Intel VROC SATA, dedicated iLO-M.2 port kit, 180w external power adapter and 1/1/1 warranty for dependable plug-and-play server operation
  • WHISPER-QUIET & SPACE-SAVING: Ultra-compact mini tower design fits easily in small office spaces; supports wall, flat, or vertical placement for deployment flexibility
  • INTEGRATED REMOTE MANAGEMENT: Comes with HPE iLO 6 and embedded TPM 2.0 for secure, license-free remote server administration through shared port access
  • EXPANDABLE DESIGN: Two PCIe slots (including PCIe 5.0) and four LFF-NHP drive bays provide robust options for storage and component scalability. Features new MR408i-p controller support for enhanced storage performance

For persistent history, write the JSON records to a log collector or database instead of relying on console output. Include a monitor identifier, URL name (not secrets), status, elapsed time, error class, and timestamp. Redact authorization headers and response bodies. A small local process also needs a supervisor such as systemd, a container restart policy, or a scheduled job so it is restarted after a crash.

Fetch versus node:http

Concern Built-in fetch Low-level node:http
Status handling Inspect response.status; HTTP errors do not reject automatically. Inspect the response object’s status field.
Timeout cancellation Pass AbortSignal.timeout() as signal. Pass an AbortSignal or explicitly destroy/abort the request.
timeout option Use an AbortSignal deadline. timeout and setTimeout() notify or add timeout behavior; they do not by themselves abort the request.
Compatibility Confirm the Node version provides the Fetch API and timeout signal. Useful when you need lower-level socket, agent, or streaming controls.

Node’s HTTP documentation explicitly warns: “Setting the timeout option or using the setTimeout() function will not abort the request or do anything besides add a 'timeout' event.” If you choose node:http, attach an AbortSignal and handle the event rather than assuming a timeout ended the request.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common failures

Every check is reported as healthy for a 404 or 500

Check that the code compares response.status (or response.ok) and that MONITOR_EXPECTED_STATUS is not accidentally set to the failing code.

Requests never finish

Confirm that the signal is passed in the fetch options and that MONITOR_TIMEOUT_MS is a positive number. With node:http, setting only timeout is insufficient; abort or destroy the request explicitly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Dell PowerEdge T140 Mini Tower Server with Intel Xeon 3.3GHz CPU, 32GB DDR4 RAM, 8TB HDD Storage, RAID (Renewed)
  • Dell PowerEdge T140 Mini Tower Server for Small Businesses, Branch Locations, and Home Offices
  • Intel Xeon E-2124 Quad-Core 3.3GHz 8MB CPU, Max Turbo Up To 4.3GHz Memory: 32GB DDR4 PC4-21300 2666MHz Unbuffered Memory
  • 8TB (4 x 2TB) 7.2K 6Gb/s SATA 3.5" HDDs for High Capacity Storage; PERC S140 6Gb/s RAID Controller
  • iDRAC9 Basic; On-Board Dual Port 1Gb LOM
  • iDRAC9 Basic; On-Board Dual Port 1Gb LOM

Timeouts appear as different error names

Handle both TimeoutError and AbortError, as the example does. Log the elapsed time and classify the result as failed whenever no response was received.

DNS, TLS, or connection errors

These occur before an HTTP response exists. Keep the error name and message, verify DNS and certificate validity from the monitor’s network, and avoid logging secrets embedded in a URL.

Content check fails intermittently

The marker may be personalized, localized, loaded by client-side JavaScript, or changed by a deployment. Choose a stable server-rendered token or remove the content assertion and monitor status and latency only.

Checks overlap or consume too many resources

Use the completion-based scheduling shown above, increase the interval, and cap response-body reads. A monitor that checks many URLs should use a bounded worker pool rather than starting unbounded promises.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

If your goal is a clean visual capture rather than an HTTP availability result, ScreenshotNeo provides a single request to return a PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for all options. A one-call capture is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

Every feature is included on every plan: 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to get an API key.

Operational checklist

  • Keep the URL, allowed statuses, interval, timeout, and marker in configuration.
  • Set a timeout shorter than the interval and verify that it actually aborts.
  • Record status, elapsed time, error class, and timestamp for every attempt.
  • Test from the same network region and identity that matters to your users.
  • Redact credentials and avoid storing full response bodies.
  • Choose retries and alert thresholds based on the endpoint’s failure tolerance.
  • Run the process under a supervisor and monitor the monitor itself.

Frequently Asked Questions

How do I check if a website is up with Node.js?

Send a request, verify that its status is in your configured healthy set, measure elapsed time, and treat network or timeout exceptions as failures. The complete script above implements that flow.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I set a timeout for fetch in Node.js?

Create AbortSignal.timeout(milliseconds) and pass it as the fetch option signal. Catch the resulting timeout or abort error and record a failed check.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.