Your missing pages return 200 OK, and it is breaking things you have not noticed
This one cost two weeks on three separate sites before anyone spotted it, because every check that was being run came back green.
The symptom, if you can call it that, is nothing. The site loads. Pages return 200. Monitoring is happy. But a tool on the page quietly does nothing when you use it, and no error appears in the console.
What is actually happening
Many static hosts and single page app configurations include a catch-all rule: any path that does not match a real file gets your index.html, with status 200. That is correct and necessary for client side routing. It is a disaster for everything else.
Consider a tool that loads a dictionary at startup:
const res = await fetch('/data/words.json');
if (!res.ok) throw new Error('missing data');
const words = await res.json();
If words.json did not deploy, the fetch returns your homepage HTML with status 200. res.ok is true, so the guard never fires. Then res.json() throws a parse error inside a promise nobody awaited, and the page carries on looking perfectly normal with a tool that silently does nothing.
How to detect it in one command
Ask for something that definitely does not exist:
curl -s -o /dev/null -w "%{http_code}\n" https://yourdomain.com/zzz-does-not-exist-9912
You want 404. If you get 200, you have this problem.
Confirm by comparing checksums. If a nonsense URL and your homepage hash identically, every missing file on your site is being served as your homepage:
curl -sL https://yourdomain.com/ | md5sum
curl -sL https://yourdomain.com/zzz-9912 | md5sum
What it breaks, in order of how much it costs you
- Your tools, silently. Any
res.okcheck is defeated. This is the expensive one because there is no error to find. - ads.txt. Ad networks read HTML where a text file should be and report the file as missing.
- Search indexing. Every typo, every dead link, every stale URL becomes a real indexable page that duplicates your homepage. Search Console fills with soft 404s and duplicate content.
- Your own audits. Checking that
/aboutreturns 200 proves nothing at all. It was always going to return 200.
The fix
Add a real 404.html at the site root. Most static hosts serve it automatically for unmatched paths once it exists. If you genuinely need client side routing, scope the catch-all to your app routes only, and never to asset paths like /data/*, /*.txt, or /*.json.
Change how you verify. A 200 proves a request was answered. It does not prove the right thing answered. When checking that a page exists, compare its content against your homepage rather than reading its status code. That single habit would have caught this in minutes instead of weeks.
Related
Why AdSense Rejects Sites (And How to Actually Get Approved)
The real approval checklist, plus the silent bugs that tank it without you knowing.
ANALYTICSGA4, Search Console and Bing: The Setup Nobody Explains Clearly
Property IDs vs measurement IDs, verification, and why Google may not know you exist.
OVERVIEWWhat it actually takes to put a site together
The full sequence, the real timings, and what each step actually cost. Start here.