There is a particular kind of broken that makes your stomach drop. You load your homepage and it comes back as a column of plain text — blue underlined links, Times New Roman, every image stacked full width down the page in whatever order it happens to appear in the markup.
It looks like the site has been wiped, or hacked, or that a deploy went catastrophically wrong. It is worth knowing early that it is usually none of those. The site is intact and the server is healthy. One file — the stylesheet — isn’t arriving, and a page without its stylesheet degrades into something that looks far worse than the actual fault.
This happened here, on this site, on 18 August 2026. So the walkthrough below is the real one rather than a hypothetical.
The console said something that sounded impossible
Refused to apply style from 'https://laymance.com/_astro/index.DeMhMUTe.css'
because its MIME type ('text/html') is not a supported stylesheet MIME type
Read that carefully, because it is not saying the file is missing.
It is saying the browser asked for a stylesheet and got an HTML page back. Browsers don’t guess about this: a response arriving at a .css URL with an HTML content type is refused outright rather than optimistically parsed.
Requesting that URL directly confirmed it. Status 200. Content type text/html. And the body was the homepage — the entire homepage, served under the address of a stylesheet.
A 200 is not proof the file is fine
This is the part worth keeping even if you never touch a Cloudflare account.
Most monitoring, and most instinct, treats HTTP 200 as “working.” It isn’t. A 200 means the server successfully returned something. Whether that something is what was requested is a completely separate question, and it’s the one that gets skipped.
Here the status was 200 and the response was wrong. An uptime monitor would have reported this site perfectly healthy for the entire time it looked destroyed.
The decisive test is one request
The diagnostic that separates the two possible causes is almost embarrassingly simple. Ask for the same URL again with a meaningless query string stuck on the end.
curl -sI "https://example.com/assets/main.abc123.css" # what visitors get
curl -sI "https://example.com/assets/main.abc123.css?cb=1" # what the origin has
A query string the cache has never seen forces a fresh trip back to the origin server. So:
- If the plain URL returns HTML and the
?cb=1version returns real CSS, the file is fine and the cached copy is wrong. - If both fail, the file genuinely isn’t there, and you have a build or deploy problem instead.
Those two have entirely different fixes — which matters a great deal, because the obvious fix only works on one of them.
Redeploying does not fix a cache problem
When a site looks broken, the instinct is to push again. On this class of problem that accomplishes exactly nothing, and understanding why is most of the value here.
Modern build tools give static assets content-hashed filenames — index.DeMhMUTe.css rather than style.css — so the URL changes whenever the contents do. That’s a good design, and it’s also precisely why the redeploy is futile: if the file’s contents haven’t changed, the rebuild produces the identical hash, the HTML points at the identical URL, and the poisoned cache entry is still sitting at that address.
You will push, watch the build go green, reload, and see the same broken page. Probably twice, before you start doubting the deploy itself.
And the bad entry cannot heal itself
Content-hashed filenames invite an aggressive caching rule, and this site uses one:
/_astro/*
Cache-Control: public, max-age=31536000, immutable
A year — and immutable on top of it, which is a promise to the browser that the contents at this URL will never change, so don’t bother revalidating. Not on a reload. Not on a hard refresh.
That promise is correct for the file it was written about. For a wrong entry it’s a catastrophe, and it is exactly why hard-refreshing doesn’t clear this: you have told every browser and every edge node not to check. A bad response cached under those headers cannot expire its way out of the problem.
How something so wrong got cached at all
The rule above matches on path pattern, not on content. Anything under /_astro/* gets stamped immutable for a year, regardless of what the response actually turned out to be.
During a deploy there’s a brief window where the new HTML is live and pointing at a newly-hashed asset that hasn’t finished publishing. A request landing in that window asks for a file that doesn’t exist yet, gets answered with HTML — a catch-all 200, not a 404 — and the header rule dutifully stamps that HTML as immutable for a year.
One request, arriving inside a window a few seconds wide, cached until next August.
Worth knowing when this is possible at all: only on a deploy that changes an asset’s contents. That’s the only time there’s a new URL for a request to lose the race against. A deploy touching only templates leaves every asset hash untouched, and there is no window.
The fix, and checking the blast radius first
Purge that one URL from the cache. On Cloudflare it’s Caching → Configuration → Purge Cache → Custom Purge → by URL, and every CDN has the equivalent.
Resist Purge Everything. It works, and it also discards every good cache entry you have and sends the next wave of traffic straight at your origin. Find out how much is actually affected first — pull the asset URLs out of your own homepage and ask each one what it returns:
curl -s https://example.com/ | grep -oE '_astro/[^"]*\.(css|woff2)' | sort -u |
while read a; do
printf "%s " "$a"
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" "https://example.com/$a"
done
Here, exactly one of eight assets was poisoned. Everything else was correct — including a second stylesheet that had kept its hash through the deploy and so was never re-requested.
What generalizes
Cloudflare Pages is the worked example, but none of this is specific to it. Any CDN that applies cache rules by path pattern can stamp the wrong response as long-lived:
- A 200 is not proof the response is correct. Check what came back, not merely that something did.
- Cache rules apply to whatever was served, not to what should have been served.
- A redeploy is not a purge. Identical content produces an identical URL, and leaves the bad entry exactly where it was.
immutablemeans a bad entry can’t self-heal. Neither a hard refresh nor patience will clear it.- The cache-buster query string is the test. One request tells you whether you’re looking at a cache problem or a real one.
We left the caching rule alone. Dropping immutable would let a bad entry eventually expire on its own, but the filenames are content-hashed, so immutable is genuinely correct for them — and one race in the life of a site isn’t sufficient reason to give up revalidation-free caching. If it happens twice, that changes.
The good news is that all of this is diagnosable from a laptop with curl and a browser console. The bad news is that the symptom — a site that appears completely destroyed — is alarming enough that the natural response is to start redeploying, and redeploying is the one thing that cannot possibly help.
If your site is doing something like this and the cause isn’t obvious, that’s what support is for. And if you’d rather somebody noticed this kind of thing before you did, that’s website maintenance — the version where it’s somebody’s job to be watching.
