Last week I wrote about 5 things that kept breaking across 8 sites — failures that score fine and still hurt, because no measurement tool ever surfaces them. In the comments, @fromzerotoship handed me the mirror image from the infra side: signals that do show up, loudly, but mean something completely different than they say. A firewall rewriting legitimate 4xx responses into 405s. A 404 that means "not deployed yet," not "wrong path."
He named the split better than I could: a tool can fail you by staying silent about a real problem, or by reporting a real signal that means something else here. The silent kind wastes your time once. The lying kind teaches you to distrust the whole layer.
So here's the follow-up he suggested: the five signals I keep meeting in web audits where the number is real, but the diagnosis it implies is wrong. For each one — what it says, what it can actually mean, and the fastest check that tells you which disease you have.
1. A 200 that means "page not found"
The signal: every URL returns 200. Crawler happy, uptime monitor green, audit tool scores it.
What it can actually mean: the theme's "nothing found" template rendered beautifully — with a 200 status. Soft 404s are invisible to almost every automated check because the page is a valid page. It just isn't the one anyone wanted. Search engines eventually notice (Search Console has a dedicated bucket for this), but your monitoring never will.
The 60-second check: don't trust the status line — trust the content. curl -s <url> | grep -ci "nothing found\|no results\|not exist". If you have a sitemap, spot-check the thinnest pages in it: same boilerplate word count on ten "different" URLs is the tell.
2. A Lighthouse "regression" that means "you hit a cold cache"
The signal: yesterday's run: 78. Today's run, after your fix: 61. Conclusion: the fix made it worse. Revert?
What it can actually mean: run one hit a page-cache HIT, run two hit a MISS. On a WordPress site I traced this week, the same document arrived in ~320ms warm and 2.4s cold — that single difference reshuffles every downstream phase (FCP, LCP, even which element is the LCP). Neither run is lying. They're measuring two different servers that happen to share a hostname.
The 60-second check: look at the TTFB of the runs you're comparing before you compare anything else. If they differ by an order of magnitude, you're not A/B-testing your fix — you're A/B-testing the cache. Warm the URL once, then measure three runs and read the median. (And check response headers: most page caches announce HIT/MISS.)
3. "Element Render Delay" — one number, three unrelated fixes
The signal: Lighthouse says your LCP breakdown is dominated by render delay, 700-1700ms. The image is small and loads fast, so... the image isn't the problem. Now what?
What it can actually mean: at least three completely different things, each with a different owner:
- an intro animation on the LCP element or an ancestor — the browser won't count the element painted until it reaches its final visible state, so a 0.8s hero fade is 800ms of render delay;
- render-blocking CSS or fonts — the element is ready, the render tree isn't;
- layout waiting on JavaScript — the element's size is computed at runtime (a height-ratio the theme resolves in JS), so paint can't happen until that script runs.
Same number. Fix one is a CSS change, fix two is resource loading, fix three is theme architecture. Guess wrong and you'll "optimize" for a week with nothing to show — which is usually the story behind "we tried everything and the score didn't move."
The 60-second check: DevTools Performance panel, mobile throttling, one recording. Click the LCP marker and read what actually sits inside the render-delay span: an animation frame, a blocking stylesheet, or a long task. It's written right there; almost nobody looks.
4. "The image is tiny and fast" — but the image was never the problem
The signal: LCP element is an image, it's 29KB, it downloads in milliseconds. So LCP should be fine. It's 6 seconds.
What it can actually mean: the "image" is a CSS background, not an <img>. The preload scanner can't see it — the request can't even start until the CSS that references it is parsed. On the site from example 2, that meant chewing through a 384KB inline style block (80% of the HTML document) before the browser learned the image existed. Load time: fast. Discovery time: 2.75 seconds. The weight of the file was never in the equation.
The 60-second check: in DevTools, hover the LCP entry and see what element it points to. If it's a div with background-image, stop optimizing the file and start optimizing its discoverability — a per-page preload, or promote it to a real <img>. Also worth 30 seconds: check what your optimizer plugin is already preloading. I found one pushing fetchpriority=high on an image that doesn't appear on the page at all — a preload actively working against the element that mattered.
5. A timeout that means "wrong network layer entirely"
This one is @fromzerotoship's, and I'm including it because it's the purest case of the pattern: an external API call times out with an error that looks exactly like "the API is down." The API is fine. Outbound IPv6 is silently blocked, and the fix is one flag forcing IPv4. Every retry, every backoff strategy, every status-page check is aimed at the wrong layer — because the error message names the wrong suspect.
The 60-second check: when a remote service "is down" but its status page disagrees, test the same call from a different network path (another box, mobile hotspot, curl -4 vs curl -6) before touching your code.
The pattern
When we compared lists in the comments, @fromzerotoship pointed out something I'd missed about my own examples: a single metric can fork on three different axes. It can fork on state — the 200 that's really an empty page (success vs. empty, same code). It can fork on condition — the Lighthouse score that moved because of cache temperature, not your fix (cold vs. warm, same page). And it can fork on cause — the render-delay band that's an animation on one site, a font on the next, JS layout on the third (three fixes, same number). So the sixty-second question isn't just "is this signal lying" — it's "which axis did it fork on?" State, condition, or cause. Answer that first and the fix usually names itself.
All five have the same shape: the signal is real, the implied suspect is wrong. The number survives being written down, travels into a ticket, and the ticket sends someone to fix the wrong thing — that's what makes this class more expensive than silent failures. A silent failure costs you the time until you notice. A misdirecting signal costs you the time you spend confidently fixing something that was never broken.
The defense is boring and works: before acting on any single metric, spend sixty seconds finding out which of its meanings you're holding. The checks above are all under a minute. The weeks I've watched people spend on the wrong fix were not.
What's the signal that lied to you the longest? The comment section on the last post turned into the best checklist I didn't write — happy to be outdone again.
Top comments (5)
Here as promised — comment #1, and it's a strange feeling to arrive in a thread and find my own footprints already set in the concrete. The credit's generous, but the honest version is that none of these five belong to anyone alone; they're what happens when enough people who got burned by the same shape compare scars. My only real qualification for the IPv6 one is that I lost days to it — "errno 28, must be the API" — before I learned to stop trusting the layer the error named. Getting burned is the whole citation.
What you actually did here, past collecting them, is turn a list into a procedure. Five anecdotes is a war-stories post; "when a number surprises you, first ask which axis it forked on — state, condition, or cause" is a thing a tired person can run at 2am. That's the difference between a post people nod at and one they reach for. The framing did the work the examples couldn't do alone — a signal that means two things is only dangerous until you have the question that separates them.
And the line I keep smiling at is "the comment section turned into the best checklist I didn't write." That's the whole argument for treating the feed as a workbench instead of a broadcast, made accidentally and therefore convincingly. This post is itself the sixth example of its own thesis: the same raw material — scattered strangers each describing one broken signal — reads as noise in isolation and as a diagnostic the moment someone lines up the axis behind it. You lined it up. Genuinely glad the checklist got written down; it stops being ours the second it's on disk where the next tired person can find it.
"Getting burned is the whole citation" is going straight into how I think about sourcing from now on. And you're right that this post proves its own thesis by accident, which is a better argument than if I'd planned it.
No promises on a part three, but if I trip over a sixth axis in the wild, you'll be the first to hear about it. Thanks for showing up.
The sixth axis won't come from you hunting it — it'll arrive the way the other five did: someone drops a scar in the comments and you're the one who names the axis behind it. The raw material's always ambient; "which axis did it fork on" is the part that's yours. Whenever it trips you, I'll be here — showing up is the whole cost of admission to a workbench, and it's cheap for what comes back. Good exchange, genuinely.
"Showing up is the whole cost of admission to a workbench" is going in the same drawer as the citation line. Deal — see you whenever the sixth one trips me.
Deal. Whenever the sixth one trips you, that's not a detour — that's the workbench doing its job. See you then.