Outrings
AI agents and automation

How do I monitor a website for regressions automatically?

Catching the silent breakages — an expired certificate, a stripped header, a robots.txt that started blocking Google — before anyone reports them.

2 min read
Short answer

Uptime monitoring tells you the site is up. It will not tell you that your certificate expires in nine days, that a CDN change dropped your security headers, or that a deploy pushed a staging robots.txt blocking every crawler. Those need a periodic full audit compared against the previous one — which is what a changes endpoint does.

The failures uptime monitoring never sees

A site returning 200 can be badly broken in ways that cost you for months before anyone notices.

  • A staging robots.txt deployed to production. Disallow: / everywhere. The site is up, fast, and being progressively removed from every search index. Typically noticed weeks later via a traffic chart.
  • A certificate approaching expiry. Fine until the exact moment it is catastrophic, and then every visitor gets an interstitial.
  • Security headers dropped by an edge change. A CDN configuration update, a new WAF rule, a rewritten origin config. Your CSP is in your repository and not in the response.
  • A canonical tag pointing at staging. Every page telling search engines the real version lives on a host nobody can reach.
  • A noindex left in from a launch. The single most expensive one-line mistake in web publishing.
  • DMARC or SPF broken by a DNS edit. Silent until deliverability drops, and hard to attribute afterwards.

None of these change the status code. All of them are visible in a full audit.

The shape of the check

Run a complete audit on a schedule, compare it with the previous run, and alert on the difference rather than on the absolute state. Alerting on state means alerting constantly about things you have decided not to fix; alerting on change means you hear about exactly what moved.

curl -s "https://outrings.com/api/v1/changes?url=example.com" | jq '{
  comparable,
  scoreDelta,
  direction,
  newProblems: [.newProblems[]? | "\(.id) (\(.status))"],
  resolved:    [.resolved[]?    | "\(.id) (was \(.was))"]
}'

The endpoint re-audits, compares against the last stored snapshot, and reports what appeared, what cleared and how each category moved. On the first run it returns comparable: false — that is history starting, not an error.

A daily job

#!/usr/bin/env bash
set -euo pipefail

site="example.com"
result=$(curl -sf "https://outrings.com/api/v1/changes?url=$site")

comparable=$(jq -r '.comparable' <<<"$result")
[ "$comparable" = "true" ] || { echo "first snapshot stored for $site"; exit 0; }

new=$(jq -r '[.newProblems[]?.id] | join("; ")' <<<"$result")
delta=$(jq -r '.scoreDelta // 0' <<<"$result")

if [ -n "$new" ]; then
  echo "REGRESSION on $site (score $delta): $new" >&2
  exit 1
fi

echo "$site unchanged (score $delta)"

Run it from cron, a scheduled CI job, or any scheduler you already have. Exit code 1 on regression is what makes it composable with everything else.

Daily is the right default for most sites. Hourly rarely tells you anything a daily check would miss, and each audit makes dozens of requests to your own origin — there is no reason to pay that cost more often than your deploy cadence justifies.

What to alert on, and what to ignore

SignalAlert?Why
A new critical or high findingYesSomething broke since the last run
Score dropped more than a few pointsYesUsually several small regressions at once
Certificate expiring within 21 daysYesThe one deadline that is absolute
Indexability changed at allYesnoindex or a blocking robots rule is high-cost and easy to miss
Score moved by under a pointNoContent churn, not a regression
A new low-severity findingNoBatch these into a weekly review

Attributing a change correctly

When a score moves, there are three possible causes and only one is worth waking up for: the site changed, the ruleset changed, or a check that previously succeeded is now undetermined because the auditor was blocked. Every response carries versions.ruleset — if that differs between two runs, compare like for like before concluding anything. And a drop accompanied by new undetermined entries usually means bot protection started intercepting the audit rather than that anything regressed.

What our audit reports about this

Every item below is measured directly, not inferred. Run it against your own site and the result names the exact rule or header responsible.

  • A changes endpoint that re-audits and reports new problems, resolved findings and per-category deltas against the previous snapshot.
  • Stored snapshot history per site, readable without re-fetching, so it still works while the site is down.
  • Certificate expiry as a dated field rather than a pass/fail, so you can alert on a horizon rather than on failure.
  • Ruleset and schema version stamps, so a score change can be attributed to the site rather than to the rules.

For agents and scripts, the same measurement is at /api/v1/summary?url=yoursite.com — see the API documentation.

Related questions

How is this different from uptime monitoring?

Uptime answers whether the server responded. This answers whether it responded correctly — headers, certificate, crawler rules, indexability, email authentication. A site can be perfectly available and quietly deindexing itself.

How often should I run it?

Daily suits most sites, or on every production deploy if you ship frequently. More often than that mostly adds load on your own origin without surfacing anything new.

What does comparable: false mean?

There is no earlier snapshot to compare against, so this run became the baseline. It is the expected result on a first run and not an error.

Will it catch a change my CDN made?

Yes, and that is one of the main reasons to run it. The audit reads the response as served to the public, so a header your origin sets and your edge strips shows up as missing — which is the truth that matters.

Read next

All 100 guides · How every check works · API for agents