Weekly impressions, five weeks in a row: 3,913. Then 3,289. Then 2,068. Then 80. Then 2.
I want to walk through this one properly, because the diagnosis took two months and I spent most of that time confidently fixing the wrong layer. The ending is not tidy. I will get to that.
Context: I build a desktop file converter. The site around it is mostly programmatic, a few hundred pages shaped like "convert X to Y". That detail matters later.
The first thing I did was look at average position, because that is the graph everyone checks. It had barely moved: 68 to 76. Nothing to see.
That was wrong, and the reason is worth internalising. Average position is survivorship-biased. When a page falls past roughly 100 it stops reporting entirely, so it exits the average instead of dragging it down. A site can lose everything while its average position sits flat and even improves.
The number that actually moved was distinct queries: 846 down to 37.
Flat position with a query cliff is a specific signature. It does not mean you were outranked. It means you were removed from the set of pages eligible to rank at all. Those are different problems with different fixes, and they look identical on the graph I checked first.
So: always pull distinct queries alongside position. Position tells you how you did among the pages that competed. Query count tells you whether you competed.
I had deleted 297 thin pages. That part was correct, they were genuinely weak.
I wrote their 301 redirects into public/_redirects.
Two things were wrong with that. It is the wrong layer for my setup (my framework needs to compile redirects into the deployed worker, and a static file in public/ is just a file it copies), and my deploys were manual at the time, so that commit never went out anyway. Either mistake alone would have done it.
Production served 404 on 297 URLs for about two weeks.
This is the part I had not seen written up anywhere, so it is the part I would most want to read.
Fixing a mass-404 does nothing on its own, because the crawler that would notice has already left. My crawl rate went 222 URLs per week, then 23, then 1. And my sitemap had last been downloaded on July 20, which is before the breakage.
So Google's picture of my site was frozen at a snapshot that predated the damage, it was fetching roughly one page a week, and every repair I shipped was landing somewhere nobody was looking. I could have fixed it perfectly on day one and seen the same flat zero.
I spent a while ruling out the scary explanations, each with evidence rather than vibes: Googlebot got 200s, robots.txt allowed everything, no core update had been announced, the CTR was normal for position 68, and there was no manual action. Good. But ruling things out is not progress, and I mistook it for progress for about a week.
I resubmitted the sitemap. Verified it registered. Waited. Still zero.
Two reasons, both embarrassing in hindsight.
I submitted the index, not the child. My sitemap-index.xml is a pointer file listing one child, sitemap-0.xml, which holds the actual URLs. Google fetched the index in about a second and never traversed to the child. So the file with all my URLs in it sat on a stale list of 848 URLs, from July 21, for 22 days.
Submitting a sitemap index does not make anything re-read its children. Submit the child too.
My sitemap had zero lastmod tags. Google had no signal anything had changed, and no reason to re-read a file it believed it already knew.
Adding lastmod has a trap in it, which I hit: the obvious implementation is new Date() at build time, and that restamps every URL on every no-op deploy, which trains the crawler to discount the hint entirely. I set it from a fixed constant instead, bumped by hand when content actually changes.
Then that constant went stale three separate times, once across five content PRs, because nothing enforced bumping it. The cost was specific: my best link-earning asset, an original research piece, shipped two days after Google's last sitemap read and sat at "URL is unknown to Google" while I assumed it was live.
There is a smaller, funnier failure inside this one. I had written a script to check exactly this, comparing the URL set Google holds against the live file. It printed a green check the whole time. When I finally read it, a branch for the multi-child case meant the comparison loop never executed on a single-child sitemap, so it was passing vacuously. The tool written to catch the trap had the trap in it.
Submitting the child directly worked. Google downloaded it the same day. Crawl climbed from 1 URL per week to about 24, then to about 40.
And indexing got worse the whole time.
I measured a fixed panel of 164 URLs, the same deterministic sample, twice, twelve days apart, comparing per URL rather than in aggregate. On August 20, 38 of them were indexed. On September 1, it was 24. The indexed rate went from 23.2% to 14.6%.
Then I split that by whether Google had re-crawled the page in between.
Of the indexed pages Google re-crawled, 14 of 14 were dropped. Of the indexed pages it did not re-crawl, 0 of 24 were dropped. Nothing moved the other way.
More crawl budget was shrinking my index. Every visit was a re-evaluation, and I was losing every re-evaluation.
Honest caveat: some of those can report as "crawled, currently not indexed" transiently while being re-assessed, so part of that 14 may be lag rather than a verdict. But the direction has held across three separate reads, 33% to 27.9% to 14.6%, and nothing has recovered.
The thing I finally accepted from this: plumbing and quality are different problems, and I could not tell them apart for two months. I ran three full technical audits. All 657 sitemap URLs return 200. All 657 self-canonical. Zero noindex. Lighthouse SEO 100. Median 919 words per page. The site is clean, and it is being de-indexed anyway, which means the remaining levers are authority and content selection, not anything I can fix in a config file. I have zero inbound links. That is the actual story and it took a self-inflicted outage to make me look at it.
The part that keeps me sane. On the same site, over the same weeks:
Google has about 28% indexed at average position 58 to 71, sending roughly nothing. Bing has about 61% indexed at average position 6, and is the only engine sending me actual humans.
Bing also ranks the pages Google refuses outright, and it ranks them for queries that read like someone thinking out loud:
Google, same period, surfaces me only for bare format names like "jxl" at position 95.
I have no clean theory for why. I just no longer assume one engine's verdict is the truth and the other is a rounding error.
Here is the thing I would resent a post like this for leaving out.
The peak I lost was about one click per day, at average position 65. Those 3,913 impressions were page 5 to 9 impressions. The collapse looks catastrophic on a chart and cost me almost nothing in absolute terms, because there was almost nothing there.
What it actually cost was two months of believing I had a technical problem, when the whole time I had a nobody-links-to-you-and-your-pages-are-mediocre problem. The outage was a very loud distraction from a quiet fact, and I was grateful for the distraction, because plumbing is fixable in an afternoon and authority is not.
I do not have a happy ending. Crawl is healthy, the sitemap is current, the gate that stops the constant going stale a fourth time is shipped, and the index is still shrinking. I re-measure the same 164-URL panel in two weeks. If it is still falling, the answer is not another audit.
If you take one thing: when traffic drops, check distinct queries before you check position, and check when your sitemap was last actually downloaded before you fix anything. Those two numbers would have saved me about six weeks.
The plumbing diagnosis seems pretty well established now. The harder question may be what Google is telling you about the remaining 657 pages: if crawl is healthy but re-crawling consistently removes pages, does that push you toward changing the page strategy rather than trying to recover the index?
Yes, though Google isn't really what's pushing me there.
One caveat on the re-crawl signal: crawl is recovered rather than healthy, about 40 URLs a week against 222 before the breakage, so Google's view of the site is roughly a month stale. And my sampler was striding over a sitemap that grew underneath it, so the panel wasn't comparable between reads. On the URLs present in both, it's flat.
What pushes me is what the other channels say. On Bing the same pages are 89% indexed at a mean position of 6.4, all page one. So it isn't that the pages are unrankable.
And the referral data is the part I didn't expect. Every non-search visit this site has ever recorded came from an AI assistant. Three of three, across ChatGPT, Copilot and Doubao, with zero conventional referrals, which tracks since we have zero backlinks. Tiny numbers, but assistants are already sending about a fifth of what every search engine combined sends, on a site Google has 14% indexed. Copilot is Bing-backed, so the engine that indexes us is the one feeding the assistants.
So yes, page strategy, but the target moved. Being quotable to a model that already has us indexed looks like a better bet than recovering an index that keeps saying no.
That shift in the target is much more interesting than the indexing problem itself. I’d be interested in digging into what “quotable to a model” actually looks like in practice. Happy to continue privately — what’s the best email to reach you on?