DEV Community

Sungwoo Lee
Sungwoo Lee

Posted on

65 Tool Pages, 12 Pageviews in 30 Days: The Bug Was in the Site Graph

I shipped 65 calculator pages to a content site over a few months. Last week I finally pulled the analytics for them: 12 pageviews across all 65 pages in 30 days.

Not 12 per page. Twelve total.

Every page returned HTTP 200. Every page was in the sitemap. The search console had them. The keyword demand was real — the Korean search-ad planner reports roughly 898,000 monthly searches for the single term "character counter" and 604,000 for "percentage calculator." Supply existed, demand existed, and the two never met.

This is what I found and what I changed.

First: rule out the boring failures

Before theorizing, I checked the things that are cheap to check.

Are they up? I pulled every tool URL from the sitemap and requested all 65 with cache-busting. 65/65 returned 200. One returned a connection error on the first pass and 200 twice on retry — a transient blip, not a pattern.

Pulling the URL list from the sitemap rather than grepping the source turned out to matter. My first instinct was grep "slug:" tools_calc30.js, which dumped 622 KB into my terminal because that file embeds a large JSON dataset in a string literal. The sitemap is the artifact that search engines actually consume, so it's also the correct inventory to audit against.

Are they indexed? Partially. Impressions were rising — 30/day at the start of the month to 162 six days later, average position moving from 14.3 to 6.6. So crawling wasn't blocked.

Are they slow? Median around 0.9s, served from the edge. No.

So the pages were reachable, crawlable, and improving in impressions. And nobody was reading them.

Then: query the site graph

The thing I hadn't checked was whether anything on my own site linked to them.

SELECT COUNT(*) FROM posts WHERE content_html LIKE '%/tools/%';
Enter fullscreen mode Exit fullscreen mode

Zero. Out of 380 published articles, not one contained a link to a tool page.

The only path from the site root to any tool was a single "Free tools" item in the top navigation. From there, an index page. From there, a tool. So every tool page sat two to three hops from the homepage, reachable only through a nav link that search crawlers weight very lightly and that human readers, deep in an article about something else, never see.

I had built a wing of the building and forgotten to cut a door into the hallway.

Why "it's in the sitemap" wasn't enough

A sitemap tells a crawler a URL exists. Internal links tell it the URL matters, and they tell a human reader it exists at the moment they'd want it.

The impressions data showed the crawler had done its part: it found the pages and started testing them in results. What it hadn't gotten was any signal that these 65 pages were more than a dusty appendix. Meanwhile the humans reading my articles had no in-context path to a calculator at the exact moment a calculation would have been useful.

Two failures, one cause.

The fix

One: link from the homepage directly. The homepage is the strongest page on any small site. I added a section listing 12 tools by name and description, ordered by measured monthly search volume, plus a link to each of the two tool index pages. That takes the crawl distance from three hops to one for the twelve highest-demand tools:

const HOME_TOOL_PATHS = [
  '/tools/char-count', '/tools/percent', '/tools/date-calc', '/tools/salary',
  '/tools/loan', '/tools/age', '/tools/pyeong', '/tools/unemployment',
  '/tools/severance', '/tools/weekly-holiday', '/tools/deposit', '/tools/discount'
];
const homeTools = HOME_TOOL_PATHS
  .map((p) => TOOL_LIST.find((t) => t.path === p))
  .filter(Boolean);
Enter fullscreen mode Exit fullscreen mode

Names and descriptions come from the same array the tool pages use. Writing them twice guarantees one copy goes stale.

Two: make the per-article tool cards context-aware. Articles already rendered a small "related tools" block, but one blog section was falling through to a generic default that offered a basal metabolic rate calculator to people reading about government startup grants. An irrelevant link isn't a neutral link — it's an exit. I mapped that section to the tools its readers would actually want: take-home pay, loan interest, withholding tax, percentage, date difference.

Three: tell the index services. After deploying, I submitted the homepage, both index pages, and the twelve tool URLs through IndexNow (15 accepted), and requested indexing for the highest-value pages in the search console. That console has a small daily quota — I got three URLs through before hitting it, which is worth knowing before you plan a batch of sixty.

What I'd check first next time

The diagnostic that found this took one SQL query, and I ran it months later than I should have. So the check I'm adding to my own list is blunt:

For any new section of a site, count the internal links pointing into it from existing content. If the answer is zero, nothing else about that section matters yet.

Crawl status, page speed, structured data, and title copy are all downstream of that. I spent real time on the downstream things while the number was zero.

The counterpart to this is a rule I already believed and failed to apply: measure the thing you actually care about, not the thing that's easy to measure. "65/65 return 200" felt like a green light. It was answering a question nobody was asking.

I'll post the follow-up numbers once there's enough data to say whether the fix moved anything. If you want to see the pages themselves, they're at my-blog.org/tools — mostly Korean-language calculators, but the take-home pay one is a reasonable example of the shape.

Top comments (0)