The Silent Cloudflare Pages Bug That Was Quietly Hurting My SEO
While building a broken-link checker for Bracketly (a small free dev-tools site), I found something I wasn't expecting: every nonexistent URL on the site — /literally-anything-i-typed, /tools/does-not-exist, whatever — was returning HTTP 200, with the homepage's content, instead of a 404.
Why this happens
Cloudflare Pages has a fallback behavior for unmatched routes: if your deployed site has no 404.html, it serves index.html for any path it can't otherwise resolve — but critically, it does this with a 200 status code, not 404. This is the classic SPA-router pattern (makes sense for a client-side-routed React/Vue app where you want every path to load index.html and let JS take over routing). It's the wrong default for a plain static multi-page site, and if you never explicitly add a 404.html, you get it silently, with zero warning.
I only noticed because I was building a script to curl every URL in my sitemap and flag non-200 responses — and a deliberately-broken test URL came back green.
Why it's worse than it looks
This is what's sometimes called a "soft 404" — a page that should be an error but returns success. Search engines treat this specially, and not kindly:
- Google's crawler explicitly detects soft-404 patterns (repeated identical content served at different URLs with a 200 status) and can flag the pattern site-wide, not just the one URL
- Every broken/mistyped/old link pointing at your site becomes a duplicate-content page in Google's eyes, all serving the same homepage
- It dilutes whatever ranking signal your actual homepage has, since Google now sees N copies of it at different URLs
For a site that had literally zero custom 404 handling, this meant any crawled dead link — a typo'd URL, an old removed page, a bot probing random paths — was quietly generating homepage duplicates in the index.
The fix
Trivial once you know to look for it. In Astro (or basically any static site generator), you just need a page that generates 404.html:
---
// src/pages/404.astro
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="Page Not Found" description="...">
<h1>404</h1>
<p>This page doesn't exist.</p>
</BaseLayout>
Astro (and most SSGs) automatically emits this as dist/404.html, and Cloudflare Pages automatically serves it with a genuine 404 status for any unmatched route once it exists — no config needed, just the file being present.
$ curl -o /dev/null -w "%{http_code}" https://example.com/nonexistent-page
# before: 200
# after: 404
The lesson
If you're deploying a static site to Cloudflare Pages, Vercel, Netlify, or similar — check right now whether a random broken URL on your site returns 404 or 200. It's a two-second check and a five-minute fix, but it's exactly the kind of thing that never shows up unless you go looking, since the page looks completely normal (it's literally rendering your homepage) — there's no visible error to notice.
Source for the actual fix: https://github.com/GRimkiller360/bracketly
Top comments (0)