On a Friday in August, one file on this website became zero bytes long. Not deleted — emptied. The server went on serving it with a 200 OK and a text/javascript content type, exactly as it had the day before. The only difference was that the response body was empty.
Nothing alerted. Uptime monitoring stayed green for three days, because from a monitor's point of view nothing was wrong: the request completed, the status code was correct, the response arrived quickly. The website, meanwhile, was rendering as an empty background with a working scrollbar.
I want to write this one up properly, because the failure is more interesting than it first looks and because almost everything published about it is written by monitoring vendors describing the problem in the abstract. This is a specific incident with specific causes.
Why an empty file emptied the page
The site hides content before it shows it. That is a common pattern and on its own it is fine: elements that animate into view start at opacity:0, and a small script adds a class to the <html> element so that the hiding rule only applies when JavaScript is available. Visitors without JavaScript see everything immediately, which is the point of doing it that way.
The flaw was in what un-hides it. That job belonged entirely to one script file, fetched separately over the network. So the sequence was:
- HTML arrives. All the content is there, in the source, readable.
- The inline script runs and marks the page as JavaScript-capable.
- CSS immediately hides around thirty elements — on the homepage, essentially everything.
- The script that was supposed to reveal them arrives empty.
Step two is the trap. The guard protected against JavaScript being switched off. It did nothing about JavaScript being switched on and then failing. Once the page had declared itself capable, the only thing in the world that could show the content was a file that no longer had anything in it.
The content was never missing. View-source showed the full page. A crawler reading raw HTML would have seen every word. What a human saw was a gradient and a scrollbar whose length promised a page that was not visibly there.
The part that made it look random
Here is the detail I did not expect, and the reason the fault took days to pin down rather than minutes.
The site sits behind a CDN that caches static assets per edge, and edges refresh independently of one another. After I repaired the file, I requested the same URL three times in a row from the same machine and got three different answers:
try 1: 200 23353 bytes
try 2: 200 0 bytes
try 3: 200 0 bytes
One edge had the good file. Others were still holding the empty one, and would hold it until their cache expired. Which edge answered depended on which one you were routed to, which is why the fault moved between pages, between devices, and between one refresh and the next. It is also why "clear your cache and try again" appeared to work: it did not fix anything, it just gave you another roll of the dice.
A cached failure is worse than a live one. A live failure is at least consistent enough to reproduce.
What monitoring actually needs to check
Status-code monitoring answers one question: did the server respond? That is a real question and worth asking, but it is not the question most site owners think they are paying for. They think they are asking: is my website working?
Three checks close most of the gap, and none of them are expensive:
1. Assert on content, not on status
Pick a string that only appears when the page has genuinely rendered — a headline, a price, a phone number — and fail the check when it is absent. If your monitor only supports status codes, it is not monitoring your website, it is monitoring your web server.
2. Assert on size, not just presence
This is the check that would have caught my outage on day one. Every file the site cannot render without has a size below which it is certainly broken. A stylesheet is not 400 bytes. A bundle is not zero. A size floor costs nothing and catches truncation, partial writes and failed deploys, all of which return a perfectly valid 200.
3. Check what is served, not what is on disk
My first fix looked complete because the file on the server was correct. It was the CDN that was still wrong. If a CDN sits in front of your site, the file on disk and the file your visitors receive are two different facts, and only one of them matters to them. Fetch the public URL several times before you believe it.
I now run a check that does all three and requests each critical asset five times, because a single request only tells you about one edge and the failure mode was that edges disagreed.
The design lesson, which is bigger than the bug
I could have stopped at restoring the file. That would have fixed the symptom and left the actual problem in place, which is that the site had a single point of failure between its content and its visitors.
The rule I settled on is short: content must win over animation. Losing a fade-in is a cosmetic disappointment. Losing the page is a business event. Any time those two are in tension, the animation loses.
In practice that meant three independent layers, deliberately not sharing a dependency:
- An
onerrorhandler on the script tag, which un-hides everything the instant the request fails outright — a 404, a dropped connection, a blocked domain. - A short timer in the inline script that un-hides everything unless the real script has explicitly reported that it is working. This is the layer that catches an empty file, a truncated file, a timeout, or a script that loads and then throws.
- A CSS animation with a delay, which forces the content visible using no JavaScript at all. It exists for the case where the main thread is wedged or scripts are stripped by something between me and the visitor.
The signalling detail matters more than it sounds. The script reports success from inside the callback that actually reveals content, not at the point where it finished setting things up. Constructing an observer proves nothing; delivering content proves something. If I had signalled at setup, a broken-but-present script would have silenced all three fail-safes.
Healthy pages behave exactly as before. The animations are untouched. The layers only do anything on a page that is already failing.
How to test this on your own site in two minutes
You do not need my architecture to run the experiment, and I would rather you found this out on a Tuesday than during a campaign.
- Open your site with JavaScript disabled in the browser settings. Is the content readable?
- Open developer tools, go to the network tab, and block your main script file. Reload. Is the content readable?
- Throttle the connection to slow 3G and reload. Does the content appear at all, or only after the script lands?
- Fetch your main CSS and JS URLs from the command line a few times and compare the byte counts.
If the page is blank in tests one or two, your content depends on a network request succeeding, and you have the same fault I had. That is worth knowing whether or not it has bitten you yet.
It is also an SEO problem, though a narrower one than people assume. Google renders JavaScript, so a script that merely loads slowly is usually survivable. A script that fails is not, and neither is a page that only becomes visible after a request that a crawler might not wait for. Related failures — pages that are technically present but practically unreachable — are the subject of the technical SEO mistakes I keep finding in audits, and the reason so many Nepali sites are affected is covered in the 34-site audit.
What I would tell a client
Uptime is not the same thing as working, and a green dashboard is not evidence. If a monitor can be satisfied by a server that returns nothing, it will be, and it will keep telling you everything is fine for as long as you let it.
Check the bytes. Check the words on the page. Check what the CDN is handing out rather than what you put on the disk. And build so that when a file goes missing — and eventually one will — the worst thing your visitors lose is a nice fade.
If you want the same checks on your own site, that is part of what I do as an SEO specialist and website developer.