Web Development · SEO
We Audited Our Own 800-Post Blog. Seven Numbers That Were Lying to Us.
A full audit of an 800-post technical blog: 74% of tag hubs were noindexed and in the sitemap anyway, 85% of titles broke the share card, and Search Console reported 0 indexed pages on a site earning 35,000 impressions a month. What was actually wrong, and how to check your own.
Anurag Verma
7 min read
Sponsored
We run a technical blog with 800 published posts. Over the last month we audited it properly for the first time: every page, every tag, every sitemap entry, checked against what Search Console actually reports rather than what the dashboards imply.
Seven things were wrong. None of them threw an error, none appeared in any report as a problem, and two of them were the measurement tools themselves reporting numbers that were not true.
This is the full list with the real figures, because posts like this are usually written with the numbers filed off.
1. The API says 0 pages are indexed. The field is deprecated.
The Search Console Sitemaps API returns a contents array with submitted and indexed counts. Ours reads:
submitted = 1105
indexed = 0
On a blog earning about 35,000 impressions a month, from 799 distinct pages that appeared in search results in the last 28 days.
Google deprecated the indexed count and left the key in the response. It returns 0 for everyone, always. We built a baseline report on that API and the first version confidently printed a coverage gap of 1,105 pages, which would have started a week of debugging a problem that did not exist.
If you want real coverage, count distinct pages that received impressions and compare that to submitted. Ours is 799 of 1,105, about 72%. That is a number you can act on.
2. Impressions change by 6x depending on how you group them
Same property, same dates, one query grouped by page and one grouped by query,page:
| grouping | impressions |
|---|---|
page | 35,428 |
query,page | 6,068 |
Google anonymises rare queries and drops them from any result that includes the query dimension. On a corpus of long-tail technical posts, most queries are rare, so most of your traffic disappears the moment you ask which searches produced it.
Neither number is wrong. They answer different questions. The failure is comparing them, or building a CTR figure from the query-grouped set and describing it as sitewide. Ours was 19% of actual impressions.
3. 636 tag pages asked to be crawled so they could say “do not index me”
Our tag template noindexes any hub with two posts or fewer. That is a reasonable rule: 74% of our 856 tag hubs have one or two posts, and they are near-duplicate aggregation pages.
The sitemap listed all 856 anyway.
So 636 URLs, more than half the sitemap, existed to invite a crawler to fetch a page whose only instruction was to go away. The noindex was right. The sitemap was right in isolation. Nothing compared them, because each looked sensible on its own.
Removing them took the sitemap from 1,742 URLs to 1,099.
4. A 30-post hub had never been in the sitemap
/blog/tag/api covers 30 posts, is fully indexable, and had never once appeared in our sitemap.
The cause was a filter written to keep generated endpoints out:
if (page.includes('/og/') || page.includes('/api/')) return false;
/blog/tag/api/ contains the substring /api/. So did nothing else we cared about, which is why it survived. /blog/tag/api-design/ was fine, because there is no slash after “api” there. One hub, silently excluded, for as long as the filter has existed.
Anchor path filters to the root:
const path = page.replace(/^https?:\/\/[^/]+/, '');
if (/^\/(og|api)\//.test(path)) return false;
5. 85% of our titles broke the share card
Our Open Graph images are generated by a function: a request comes in, Satori renders a card, the crawler gets a PNG. The title was set at a fixed 90px.
Satori does not clip an oversized child or push its siblings. It draws over them. So any title long enough to wrap past its box printed straight through the subtitle, the chips and the footer.
Our median title is 74 characters. 85% are over 60, which is roughly where the overlap starts. That is 681 of 800 posts sharing a card with text printed over text.
It survived because the card is generated on request and never appears in a build. There is no artifact to look at, no test that renders it, and no reason to open the URL. We only found it because we rendered one to check something unrelated.
If you generate share cards, write a script that dumps them to disk and look at them. Ours is 40 lines.
6. FAQ schema on 287 posts, earning nothing
35% of our posts carry FAQ schema. It is valid, it validates, and on a site like ours it produces no rich result at all.
Google restricted FAQ rich results in August 2023 to well-known authoritative government and health sites. The markup is still worth having for AI answer engines, which do read it. But we had it filed mentally under “SERP real estate”, and it has not been that for two years.
We nearly spent a day adding it to the seven highest-impression pages that lacked it. The check that stopped us took two minutes.
7. 96 MB of images on a site that never needed them
public/images/blog held 96 MB across 464 PNGs. The largest cover was 1.88 MB. The CDN did no format negotiation: a request sending Accept: image/avif,image/webp came back image/png, byte for byte.
Converting to WebP at build time: 521 images, 67.8 MB saved, the directory down to 24 MB. On the worst page the hero went from 1.95 MB to 0.11 MB.
The reason it went unnoticed is that nothing measures this by default. Lighthouse tells you a page is slow. It does not tell you that a directory you never look at has been growing for a year. We had even written about image formats and LCP while our own covers were shipping as multi-megabyte PNGs, which is its own kind of finding.
What these have in common
Six of the seven were invisible because each half of the contradiction looked correct on its own. The noindex was right. The sitemap was right. The filter was right. The card code was right. Nothing compared two sources that each made sense in isolation.
So the fix that generalises is not a checklist. It is a comparison that runs on every build and fails when two sources disagree:
- every page your sitemap lists must not be
noindex - every indexable page must be in your sitemap
- every image your content references must exist on disk
- every internal link must resolve
Ours is 90 lines of Node with no dependencies. It has caught two real regressions since we wrote it, both of which would have shipped silently, including one we introduced while fixing something on this list.
The other thing worth taking from this: check whether your measurement tools are telling you the truth before you act on them. Two of the seven items above were not site problems. They were the reporting layer being confidently wrong, and both would have sent us to fix something that was not broken.
How to check your own site
Four things, none of which takes an afternoon.
Pull the Sitemaps API and ignore indexed. Count distinct pages with impressions instead, and compare that to submitted.
Run your Search Analytics query twice, once grouped by page and once by query,page, and look at the gap. That gap is how much of your traffic is invisible to any query-level analysis you do.
Diff your sitemap against your robots meta, both directions. This is where most of what we found was hiding.
Render your share card and look at it, with your longest real title, not a placeholder.
We have made the OG card generator we rebuilt during this audit free and public, because the first thing we did with it was paste in a 120-character title and watch the old version fall apart.
Frequently asked questions
- Why does the Search Console API report 0 indexed pages?
- Because the field is deprecated. The Sitemaps API still returns an `indexed` count in its response and it is always 0, for every property, regardless of reality. Google removed the metric and left the key in place. If you are building a dashboard on that API, do not plot it, and do not raise an alert on it.
- Why do my impressions change depending on how I group them?
- Google anonymises rare queries and excludes them from any result set that includes the query dimension. Group by page and you see everything; group by query and page and you see only the queries that cleared the privacy threshold. On our corpus that is a 6x difference on the same dates, so the two figures cannot be compared to each other.
- Should I noindex tag pages with only one or two posts?
- Usually yes, and if you do, take them out of your sitemap too. A sitemap is a request for crawl attention. Spending it on a page you have already told the crawler to ignore is the contradiction that made 74% of our tag entries pointless.
- Does FAQ schema still get rich results?
- Not for most sites. Google restricted FAQ rich results in August 2023 to well-known authoritative government and health sites. The markup is still valid and still useful for AI answer engines, but if you are adding it expecting more SERP space on a normal site, you will not get it.
- How do I find problems like these on my own site?
- Compare what your pages declare with what your sitemap requests, in both directions, and fail the build when they disagree. Most of what we found was invisible precisely because nothing compared two sources that were each individually plausible.
Sources
Sponsored
More from this category
More from Web Development
R.01 Webhook Design: Signatures, Retries, and Idempotency Done Right
R.02 Node.js Is Moving to One Major Release a Year. What That Means for Your Upgrade Plan
R.03 WebMCP: How Chrome Lets a Website Expose Its Own Tools to AI Agents
Sponsored
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored