Check a Web Page for Changes#

Watch a page and get told when it changes. This job fetches the page, compares it with the copy it saw last time, and only alerts on a difference, so you are not pinged on every run.

The Script#

// watch-page.js
import fs from "fs";

const target = "https://example.com/";
const stateFile = "/watch-page.txt";

// djb2: a small, fast string hash. It walks the text one character at a
// time, mixing each into a 32-bit accumulator, and returns a short hex
// fingerprint. Deterministic (same input, same output) but not
// cryptographic: good for spotting changes, not for security.
function hash(text) {
	let h = 5381; // Seed. Any odd starting value works.
	for (let i = 0; i < text.length; i++) {
		// Multiply by 33 and mix in the next character code.
		h = (h * 33) ^ text.charCodeAt(i);
	}
	// Coerce to an unsigned 32-bit integer, then render it as hex.
	return (h >>> 0).toString(16);
}

const res = await fetch(target);
const html = await res.text();
const current = hash(html);

let previous = null;
try {
	previous = await fs.readTextFile(stateFile);
} catch {
	// First run: nothing stored yet.
}

if (previous === null) {
	console.log("Baseline stored for the first time");
} else if (previous !== current) {
	console.log("The page changed");
} else {
	console.log("No change");
}

await fs.writeTextFile(stateFile, current);

How It Works#

  • Instead of storing the whole page, the job stores a short hash of it. That keeps the state file tiny and the memory use flat.
  • The hash function is the classic djb2 algorithm. It starts from a seed and folds each character into a 32-bit accumulator, so any length of input becomes a short, fixed-size fingerprint.
  • It is deterministic: the same page always produces the same hash, which is what makes comparison possible. It is also fast, and it uses only basic arithmetic.
  • It is not cryptographic. Different pages can, in principle, collide to the same hash, and the output says nothing about the input. That is fine here: the job only needs to answer “did this change”, not to secure anything.
  • The first run records a baseline and stays quiet; later runs compare against it.
  • To add a notification, post to a webhook in the “changed” branch, as in Monitor an Endpoint’s Uptime.

Setup#

Change target to the page you want to watch, then schedule the job. Once a day (0 9 * * *) is a good default for most pages.