Two of our customer sites served a byte-identical page for the first 60 seconds after deploy
We measured something today that I had never thought to look for, and I suspect most teams are not looking for it either.
Two of our customer sites, one a mechanical keyboard store and one a perfume store, came back after their containers were recreated. For roughly 30 to 60 seconds, both of them served exactly the same page. Not a similar page. Byte for byte identical, 281475 bytes on each. The title tag carried the name of a third brand entirely, and the canonical tag pointed at a localhost address on port 3000.
The byte count is what actually cracked it. Those two sites have different databases and completely different products. Any page genuinely generated at runtime would have come out at a different size. Identical byte counts meant that response never touched a database. It was a copy of something decided at build time, before either site was provisioned.
The cause was in the build, not the runtime. We ship a single image and it is tenant neutral by design. At build time there is no niche, no store name, no base URL, because none of that is known yet. Every one of those values quietly resolved to its last-resort default, and the defaults got baked straight into the prerendered HTML. That artifact then sits in the image. Every time a container is recreated the runtime cache is cold, and until the first real render lands, the baked copy is what visitors get.
The window itself was the second surprise. The page declared an hour, 3600 seconds. The effective window was 60. One fetch inside that route was called with a 60 second revalidate, and the framework takes the minimum of the segment value and the fetch values. So the number written at the top of the file was not the number governing anything. If we had trusted the declared value we would have gone looking in entirely the wrong place, because an hour long stale window is a caching problem and a 60 second one is a cold start problem.
The fix sounds simple. If the build does not know which tenant it is building for, prerender nothing. We verified it with real builds in both modes: on a generic build, prerendered locale routes went from 64 down to 0.
But the first version of that fix went out and had to be pulled back. Taking a route out of the prerender list did not make it dynamic. It made it fall into static generation at request time, and when it tried to read searchParams it threw DYNAMIC_SERVER_USAGE and returned a 500. So we traded a wrong page for a broken page. The second version fixed it properly. Just turn off prerendering is not a safe move on its own, and I would rather say that out loud than let someone repeat it.
The part that bothers me most is not the bug. It is that a status code check was never going to catch it. The process returned a fast, clean, completely wrong 200, with another company's name in the title and a canonical pointing at localhost. If a crawler happened to arrive inside that window, what it indexed was wrong content at a wrong address, and every dashboard would still have been green.
Every deploy has an opening window where the runtime is cold and the response is whatever was decided at build time. Most of us treat that window as too short to matter. It is exactly as long as a crawler needs.
So, two questions, and I would genuinely like to hear how other people handle this.
Do you measure the first 60 seconds after a deploy separately from steady state, or does your monitoring only really start once things settle?
And does your health check assert anything about the content of the response, a title, a canonical, an identifier that proves it came from your database, or does it only check the status code?
Replies