Back to News & Insights
Web Development August 9, 2026 · 6 min read

Every Page Was Exactly the Same Size. That Was the Bug Report.

A few hundred pages on a site I run were serving the homepage to crawlers. Nothing had failed: the routes returned 200, the build exited 0, and the pages looked correct in a browser. Every layer had a fallback, and a fallback converts a failure into

Every Page Was Exactly the Same Size. That Was the Bug Report.

Search Console told me ten pages on a site I run were duplicates of each other. That report is usually noise — it fires on trailing slashes, on query strings, on pagination. I opened it expecting to close it.

The site is a React single-page app that prerenders static HTML at build time, so crawlers get real content instead of an empty . I fetched the ten flagged URLs to see what the crawler was seeing.

Not similar. Identical — down to the byte, because they were the same file. Every one of them was the homepage, served under a different URL, carrying a pointing at the homepage. Each of those pages was telling Google, in the crawler's first pass, I am not a page. I am the homepage.

The routes returned 200. The app has a catch-all rewrite, so any path serves the shell and the router sorts it out in the browser. A human visiting one of those URLs saw the correct page, fully rendered, with the correct title in the tab — because after hydration the client-side code sets all of that properly.

The build exited 0. It printed a success line and a route count, and the route count was a number nobody had a reason to question.

The prerender step had a try/catch around the part that loads the page data. When that load failed, the catch logged a warning and returned an empty array. Every downstream route was generated by looping over that array, so an empty array produced zero pages — quietly, with a success exit code.

And the pages that were missing didn't 404. They fell through to the catch-all and served the shell. The failure covered its own tracks.

So: no exception, no red build, no broken link, nothing wrong on screen. Four independent layers, each doing something individually reasonable, and the composite result was a few hundred pages that existed for humans and did not exist for crawlers.

A fallback exists to stop a failure from being loud. That is its entire job, and most of the time it is the right job. But the output of a fallback is not an error message — it is a value. A plausible one. Something shaped exactly like a correct answer.

Which means you cannot find this class of bug by looking for failures, because no failure occurred. Your logs are clean by construction. Your monitors are green by construction. The system did not break; it substituted.

So the question stops being what went wrong and becomes something much more mechanical:

Uniformity is the signal. Ten pages that should have ten different sizes had one size. That is not a subtle statistical hint — it is a screaming anomaly, and it is invisible unless you go looking for sameness specifically.

Once I started asking that question instead of hunting for errors, the same afternoon turned up three more.

Identical outputs where inputs differed. The byte counts above. Ten inputs, one output. That found the missing pages.

One half worked and the other didn't, in the same file. Two sections of the same build enriched their pages from different sources — one fetched over HTTP, one needed a bundler to read a local data file. The HTTP one produced 156 links on the live page. The bundler one produced zero. Same file, same build, same run, one worked and one didn't. That is a controlled experiment I got for free, and it isolated the cause to the bundler dependency in about a minute. Without the working half I would have been guessing at the whole pipeline.

A set difference nobody had ever computed. One page linked out to every item in a database table. The router resolved those links against a separate static file. Nobody had ever asked whether those two lists agreed. I subtracted one from the other: ten links pointed at pages that could not exist. Not because of a typo — because the two sources disagreed about spelling in six cases and about categorisation in four. Every one of those links had been manufacturing a fresh duplicate page, from an indexed page, for months.

Arithmetic against a stale premise. A migration script sitting in the repo predicted the table would end up with a particular distribution of values: eleven of one kind, a hundred and seven of another. The live table had twenty-one and ninety-seven. That ten-row gap was the entire finding — a later audit had deliberately moved ten rows, and running the older script would have reverted it. I didn't need to understand either script's reasoning to know something was wrong. The numbers didn't match, and mismatched numbers are a question.

None of those four is clever. All of them are comparisons. That is the only skill involved.

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation