What thirty seconds of checking would have caught

A catalogue of small, boring, real failures — a www that resolves nowhere, a redirect that drops to http, a response that is not actually compressed — each one cheap to see from outside.

article By 6 min read

None of the failures below is interesting. Not one requires a race condition, an unusual client, or a bad day at your cloud provider. Each shows up in a single request from outside, and each is routinely found weeks later by somebody who was trying to buy something.

This is the catalogue, with the request that finds each one. Run them by hand once; every one of them can be reintroduced later by a change you were not thinking about.

The redirect that quietly drops to http

You add a trailing slash to a URL. The server redirects to the canonical form — and the redirect target is http://, not https://, because the rule was written before TLS was terminated upstream, or because the proxy builds the redirect from X-Forwarded-Proto and nothing upstream is setting it.

The browser follows it, the HSTS header sends it straight back to https, and everything works. On a client without an HSTS entry — a first-time visitor, a crawler, an embedded client — there is one plaintext request in the middle, carrying whatever was in the URL.

curl -sI https://example.com/pricing/ | grep -i '^location'

If a Location starts with http://, you have found it. The reason this survives so long is that anything reporting only the final status code reports a 200 — the plaintext hop is in the middle. redirect-chain reports the whole sequence, hop by hop.

The www that resolves nowhere

You configured example.com. Somebody types www.example.com, because plenty of people and plenty of old links still do, and there is no A record, no CNAME, and no answer at all. Not a redirect, not an error page — a DNS failure, which the browser renders as "this site can't be reached", which reads as your company does not exist.

The reverse happens too: www resolves and the apex does not.

dig +short www.example.com
dig +short example.com
curl -sI https://www.example.com | head -1

Both names must resolve, both must serve TLS, and one must redirect to the other. dns-resolution covers the first part continuously, which matters because this often breaks at a zone edit months after launch rather than on the day you set it up.

A certificate for one hostname, serving two

Downstream of the last one. www.example.com resolves and redirects correctly, and its certificate lists only example.com. A browser shows an interstitial before it ever reaches the redirect, and a non-browser client — a webhook sender, an SDK — just fails the handshake.

echo | openssl s_client -servername www.example.com -connect www.example.com:443 2>/dev/null \
  | openssl x509 -noout -text | grep -A1 'Subject Alternative Name'

Check every hostname you serve, not just the one you use. ssl-cert-expiry and cert-chain-trust run per hostname for exactly this reason.

Every log line is a favicon 404

A browser asks for /favicon.ico when it has no cached icon for the origin, which is every first visit and every visit after the cache is cleared. If it is not there, that is a 404 per visitor, forever, in your access log and your error tracking.

The cost is not the icon. It is that one path is now a constant background rate in your 404s, and the real ones — the dead link in the footer, the asset that did not make it into the build — are a small fraction of a large number. Whether you notice them depends on somebody having excluded this path from the view they look at.

curl -s -o /dev/null -w '%{http_code}\n' https://example.com/favicon.ico

Same argument for /robots.txt and /sitemap.xml, which robots-policy and sitemap-health cover, and which also affect crawling.

Compression that stopped happening at the edge

Compression is configured. It is configured on the origin, and there is now a CDN or load balancer in front that terminates the connection itself: it may drop the client's Accept-Encoding on the way through, re-request from the origin without it, or compress only the MIME types in its own default list, which commonly covers text/html and not application/json. Nobody notices because nothing is broken — the page is just several times larger than it should be, on every request, particularly on mobile.

curl -sI -H 'Accept-Encoding: gzip, br' https://example.com | grep -i content-encoding

No Content-Encoding line means no compression. Do it for the HTML, the JavaScript bundle and an API response separately: they are frequently handled by different rules. compression is the check; page-weight catches the same problem from the other end, as a number that got bigger.

A homepage frozen for twenty-four hours

Someone sets Cache-Control: public, max-age=86400 on the homepage to make it fast. It is fast. It is also stale for twenty-four hours in every CDN node and every visitor's browser, including immediately after you deploy a fix.

The specific pain is that you cannot see it. You are testing with a hard refresh and a warm session; your customers are being served yesterday's document, and when you ask them to reload, they get it again. The worst version of this is on an HTML document that references hashed asset filenames — cached HTML pointing at asset URLs that no longer exist, which is a blank page rather than a stale one.

curl -sI https://example.com | grep -iE 'cache-control|etag|age'

The rule of thumb: hashed assets get a long max-age and immutable; HTML documents get a short one or no-cache with an ETag. cache-policy watches that this stays true, because it is exactly the kind of setting that a performance-minded change reintroduces.

One subresource still on http

The page is served over https and pulls one script, one stylesheet or one image over http. Browsers block the script silently — the console has a message and the page does not, so the feature is simply absent for everyone except the person who has the console open.

curl -s https://example.com | grep -o 'http://[^"'"'"']*' | sort -u

mixed-content is the continuous version. This is a very common outcome of a copy-pasted embed code.

Headers that only exist on staging

Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options. These arrive by configuration, configuration differs per environment, and the environment where somebody tested is not the one serving customers.

curl -sI https://example.com | grep -iE 'strict-transport|content-security|x-content-type'

security-headers and csp-policy are the checks. The failure mode is not an outage; it is that a control you believe is on has been off since the last infrastructure change.

Run the list, then stop running the list

Every command above needs nothing but curl. Running them once, today, will probably find something.

The reason not to leave it there is that none of these live in your application code. They live in a proxy config, a CDN rule, a DNS zone, a framework default — places that change without a commit to your repository, and that a migration or a new environment resets wholesale. A green result in March is a fact about March.

HarpyWatch is the boring version of this list: the same requests, made from elsewhere at a fixed interval, with one row per asset and one column per check, and an alert when a cell changes colour. You could write all of it with curl and cron. What you would then own is the part that is actually work — keeping the check logic correct as your stack changes, deciding what counts as a failure rather than a blip, and routing the result somewhere a human reads. That is the difference, and it is a maintenance difference rather than a capability one. It is also not free of failure: a checker has its own outages, its own false alarms, and its own list of URLs that goes out of date if nobody prunes it.

Cite this

The HarpyWatch team. “What thirty seconds of checking would have caught”. The Nest, HarpyWatch, 27 October 2026 UTC. https://harpywatch.com/blog/what-thirty-seconds-of-checking-catches

More from The Nest

The vulnerabilities agents write fresh

Ask an agent for a login flow and you tend to get a ?next= parameter redirected to whatever it contains. It is the obvious implementation, it is an open redirect, and the remedy was documented years before the model was trained.

article 6 min read

The debt you take on when a customer tells you first

An outage costs what it costs. Being informed of it by the person it happened to costs something separate, it accrues, and it is charged against the next incident rather than this one.

article 5 min read