How Our Static Site Shipped Pages That Silently 404'd (2026)
A static export build passes, deploys green, and serves pages that were never there. Here is the Next.js trap that ships broken pages without a single error, and the cheap monitoring that catches it.
We evaluate every tool based on published features, real-world usage, community feedback, and independent testing where possible. Affiliate commissions never influence our rankings. How we research ยท Editorial policy
The pages were live. They just were not there.
We added a batch of articles to one of our sites. Wrote the content, wired everything up, ran the build. It compiled clean. No errors, no warnings, every check green. We deployed and moved on.
Some of those articles were not actually there. The URLs returned a 404. Not a broken page, not a server error - a clean "this does not exist," served confidently to every visitor and every search crawler that came looking. And nothing in our build had said a word about it.
We only found out because a search engine crawl report showed one of the URLs flipping from 404 to 200 after we happened to fix it. That is a bad way to learn that your site has been serving holes for a fortnight. This is the trap, why a passing build does not protect you from it, and the two cheap things that would have caught it the same day.
Why a static export ships pages that are not there
Modern static site frameworks - Next.js with output export, Astro, Gatsby, Hugo - turn your code into a folder of HTML files at build time. That is what makes them fast and cheap to host. It is also what makes this failure invisible.
In our setup, every article needs three things to exist: a data file with the content, an entry in a central registry so the site knows about it, and a page file that tells the framework to actually render it at a URL. Miss the third one and something quietly bad happens. The framework does not find a page to build for that route, so it does not build one. It does not warn you that a registered article has no page. It just produces nothing for that URL and carries on.
When the request comes in later, there is no file at that path, so the server returns its 404. The build never failed because, from the framework's point of view, nothing went wrong - you simply did not ask it to build that page. The mismatch between "the site lists this article" and "the site has no page for it" is exactly the kind of gap a build step is not looking for.
Green build, broken site
This is the part that catches experienced developers, not just beginners. Every signal you normally trust said the deploy was fine. The TypeScript check passed. The content linter passed. The static export reported success and printed a tidy list of generated routes. The deploy script copied the files and exited zero.
None of those checks know what was supposed to exist. The build verifies what it was told to make, not what you intended to make. If you intended fifteen articles and told it about fourteen pages, it builds fourteen perfect pages and reports complete success. The missing one is only a problem at request time, in production, to someone who is not you.
That is the general shape of the most dangerous bugs in any deploy pipeline: not the ones that turn the build red, but the ones that keep it green while quietly doing less than you asked. A red build stops you. A silently incomplete one ships.
The dangerous deploy failures are not the ones that break the build. They are the ones that keep it green while shipping less than you intended.
What would have caught it the same day
Two cheap things, either of which would have turned a two-week problem into a two-minute one.
The first is uptime monitoring on your real URLs. A service like Better Stack (or UptimeRobot, or any monitor with a free tier) pings the pages you care about on a schedule and tells you when one starts returning a 404 or a 500. If we had a monitor on each key article URL, the first failed check would have flagged the missing page within minutes of deploy, not when a search crawler stumbled on it weeks later. The free tiers cover enough monitors for a small portfolio.
The second is a post-deploy smoke check. Our deploy already generates a sitemap listing every URL the site is supposed to serve. A short script that requests each of those URLs and fails loudly on anything that is not a 200 would have caught the gap before the deploy was even considered done. You are checking your own list against your own server - the two things that disagreed in the first place.
Error monitoring like Sentry sits alongside both. It will not catch a page that was never built (there is no running code to throw an error), but it catches the adjacent failures - the broken link that does throw, the API route that errors, the client-side exception your build also cannot see. For silent 404s specifically, uptime monitoring is the direct fix.
The discipline we added
The root-cause fix was a checklist, not a clever tool. Before any deploy, verify that every registered article has all three of its parts - data, registry entry, and page file. We wrote a tiny script that walks the data files, reads each slug, and checks that a matching page file exists. It runs in under a second and fails the build if anything is missing. The trap only works because the gap is invisible, so we made it visible.
On top of that, the post-deploy smoke check: pull the sitemap, request every URL, assert a 200. It is a dozen lines and it runs every deploy. We also keep uptime monitors on the highest-value pages so that if something rots between deploys - a certificate expiry, a config change, a provider hiccup - we hear about it from a notification rather than from a drop in traffic.
None of this is sophisticated. That is the point. The failure was sophisticated enough to slip past a full build pipeline; the fix is three boring checks that assume the build is lying to you about completeness.
When this is going to bite you
The risk scales with how your pages get created. A handful of hand-built pages you can eyeball after every deploy - you will probably notice a missing one. The danger zone is any site where routes are generated from data: a registry of articles, a CMS feeding a static build, a list of products turned into pages, anything where the count of "things that should have pages" lives separately from the pages themselves.
It also gets worse exactly when you are moving fast. Shipping one page, you check it. Shipping a batch under time pressure, you check that the build passed and trust it. The build passing is precisely the false comfort that lets the gap through.
If you run a static export with more than a handful of generated routes and you do not have either a build-time completeness check or uptime monitoring on your real URLs, you have this trap latent in your pipeline right now. The cheapest insurance is to assume your next deploy will quietly drop a page and to put one check in place that would tell you if it did.
A static export reports success for the pages it built, not the pages you meant to build. That gap can serve silent 404s to users and crawlers for weeks while every check stays green. Two cheap defences close it: a build-time check that every intended page actually exists, and uptime monitoring on your real URLs so a missing or broken page raises an alert in minutes instead of being discovered by accident. The build tells you what it made. Monitoring tells you what your visitors actually get.
Frequently Asked Questions
Because from the framework's perspective nothing is wrong. Static export builds a page for every route it is given. If a route has no page file, there is simply no page to build, so it builds nothing for that path and reports success for everything it did build. The mismatch between a registered or expected article and a missing page file is an application-level intention the build is not checking. The 404 only appears at request time in production.
Two ways, ideally both. At build time, add a check that every page you intend to publish actually has a corresponding page file, and fail the build if any are missing. After deploy, run a smoke check that requests every URL in your sitemap and flags anything that is not a 200. For ongoing coverage, put uptime monitoring on your key URLs so a page that breaks or disappears raises an alert within minutes.
Not directly. A page that was never built has no running code, so there is no exception for Sentry to capture. Error monitoring catches the failures that do execute - thrown errors, failed API routes, client-side exceptions. For a page that simply does not exist, uptime monitoring that checks the HTTP status of the URL is the right tool. The two are complementary, not interchangeable.
Yes, and the free tiers make it close to free. Better Stack, UptimeRobot, and similar services let you monitor a handful of URLs at no cost. For a solo founder, the value is not fancy dashboards - it is finding out that a key page is down or returning a 404 from a notification rather than from a user complaint or a quiet drop in search traffic weeks later.
A short script that runs right after deployment and verifies the basics work - typically by requesting a set of important URLs and confirming each returns a healthy 200 response. Pointing it at your sitemap is convenient because the sitemap already lists every URL the site claims to serve, so the check compares your stated pages against what the server actually returns. Anything that disagrees is exactly the kind of silent gap that ships otherwise.