<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Toolore</title>
    <description>The latest articles on DEV Community by Toolore (@toolore).</description>
    <link>https://dev.to/toolore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4140950%2Fe8d12879-5f69-4d4b-8372-36249a4563b6.png</url>
      <title>DEV Community: Toolore</title>
      <link>https://dev.to/toolore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/toolore"/>
    <language>en</language>
    <item>
      <title>My domain came with 957,000 ghost URLs. One robots.txt line fixed it.</title>
      <dc:creator>Toolore</dc:creator>
      <pubDate>Thu, 24 Sep 2026 09:58:56 +0000</pubDate>
      <link>https://dev.to/toolore/my-domain-came-with-957000-ghost-urls-one-robotstxt-line-fixed-it-3fhp</link>
      <guid>https://dev.to/toolore/my-domain-came-with-957000-ghost-urls-one-robotstxt-line-fixed-it-3fhp</guid>
      <description>&lt;p&gt;Six weeks after launching a static Next.js site, Google had indexed exactly &lt;strong&gt;1 page out of 66&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Search Console said Google knew about &lt;strong&gt;957,000 URLs&lt;/strong&gt; on the domain.&lt;/p&gt;

&lt;p&gt;I had built sixty-six.&lt;/p&gt;

&lt;h2&gt;
  
  
  The report that actually explains things
&lt;/h2&gt;

&lt;p&gt;The sitemap report said &lt;code&gt;Success&lt;/code&gt;, 66 discovered pages. That felt fine, and it is the report most people stop at. It only means Google fetched the XML file — it says nothing about indexing.&lt;/p&gt;

&lt;p&gt;The Pages report says what is wrong:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;th&gt;Pages&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Crawled – currently not indexed&lt;/td&gt;
&lt;td&gt;621,728&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Soft 404&lt;/td&gt;
&lt;td&gt;335,238&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not found (404)&lt;/td&gt;
&lt;td&gt;358&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discovered – currently not indexed&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indexed&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row is the site I built. The rest is somebody else's.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a million URLs come from
&lt;/h2&gt;

&lt;p&gt;The domain had a previous life as an e-commerce store. Google's index does not forget quickly, and it was still crawling URLs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://toolore.com/?products/7899130/
https://toolore.com/?ctg/search/similarImageSearchResultView/?ctgItemCd=77118274
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at where the &lt;code&gt;?&lt;/code&gt; is. These are not paths — they are &lt;strong&gt;query strings on the homepage&lt;/strong&gt;. My static site, quite correctly, served the homepage with a &lt;code&gt;200&lt;/code&gt; for every one of them.&lt;/p&gt;

&lt;p&gt;So Google saw ~335,000 pages that looked like the homepage wearing different hats (soft 404s), plus ~622,000 more it had crawled and declined to index. Crawl budget is finite. It was being spent on a store that no longer exists, and my actual pages sat in "Discovered – currently not indexed", which means &lt;em&gt;we know this URL exists, we have not got round to fetching it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A separate 358 URLs were the old store's real product pages, like &lt;code&gt;/products/hunter-specialties-dod-buck-bark-deer-call/&lt;/code&gt;. Those returned a genuine 404, which is exactly right, and I left them alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;No page on my site reads a query parameter. Not one. So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;Agent&lt;/span&gt;: *
&lt;span class="n"&gt;Allow&lt;/span&gt;: /
&lt;span class="n"&gt;Allow&lt;/span&gt;: /&lt;span class="n"&gt;icon&lt;/span&gt;.&lt;span class="n"&gt;svg&lt;/span&gt;
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /*?

&lt;span class="n"&gt;Sitemap&lt;/span&gt;: &lt;span class="n"&gt;https&lt;/span&gt;://&lt;span class="n"&gt;toolore&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;/&lt;span class="n"&gt;sitemap&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Next.js that is &lt;code&gt;app/robots.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-static&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;robots&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Robots&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// The favicon is served with a cache-busting query;&lt;/span&gt;
      &lt;span class="c1"&gt;// the longer rule wins.&lt;/span&gt;
      &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/icon.svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;disallow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/*?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://toolore.com/sitemap.xml&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details worth knowing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check what else carries a query string.&lt;/strong&gt; My favicon is served as &lt;code&gt;/icon.svg?icon.2dadt9otxkdjj.svg&lt;/code&gt;. Blocking it would have cost the favicon in search results. Google resolves conflicting rules by rule length, so &lt;code&gt;/icon.svg&lt;/code&gt; (9 characters) beats &lt;code&gt;/*?&lt;/code&gt; (3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop the &lt;code&gt;Host:&lt;/code&gt; directive&lt;/strong&gt; if a generator adds one. Search Console flags it — only Yandex reads it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Three things I nearly did instead, all wrong
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Redirecting the ghost URLs to the homepage.&lt;/strong&gt; This feels helpful and creates a soft 404 for every one of them. Google treats a redirect to an irrelevant page as a soft 404, which is the problem I already had.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the Removals tool.&lt;/strong&gt; It hides URLs from results for six months. It does not remove them from the index, and doing it 957,000 times is not a plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hitting "Validate Fix" and waiting for a green tick.&lt;/strong&gt; I ran it anyway. It will fail — once the URLs are blocked by robots.txt, Google cannot re-crawl them to confirm anything. That is the intended outcome, not an error. The number to watch is Indexed, not the validation badge.&lt;/p&gt;

&lt;h2&gt;
  
  
  While I was in there: lastmod
&lt;/h2&gt;

&lt;p&gt;My sitemap stamped &lt;code&gt;lastmod&lt;/code&gt; with the build time. Every deploy told search engines that all 94 pages had just changed. Do that often enough and the field stops being believed — for the pages that really did change, too.&lt;/p&gt;

&lt;p&gt;So each page now carries the date its content actually changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toolUpdated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;CONTENT_BASELINE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sitemap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;allTools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;absoluteUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;toolPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="na"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;toolUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;changeFrequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;monthly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="c1"&gt;// …&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That also made IndexNow straightforward: after a deploy, read the live sitemap, take the URLs whose &lt;code&gt;lastmod&lt;/code&gt; is today, and submit only those. Bing and Yandex pick them up in minutes instead of days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it stands
&lt;/h2&gt;

&lt;p&gt;Honest answer: too early to claim a win. Search Console data runs several days behind, and the report I am looking at today still predates the fix.&lt;/p&gt;

&lt;p&gt;What has moved so far: "Discovered – currently not indexed" has fallen from 65 to 55, which means Google has started fetching pages it previously only knew about. Indexed is still 1. I will post an update when there is a real number rather than a hopeful one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell myself before buying the domain
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the history.&lt;/strong&gt; The Wayback Machine takes thirty seconds and would have shown me an outdoor-gear store sitting on this domain a year earlier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open the Pages report first.&lt;/strong&gt; Sitemap "Success" means the file parsed. Nothing more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your site answers &lt;code&gt;200&lt;/code&gt; to anything, that is a URL Google can index.&lt;/strong&gt; Query strings on a static site are the easy way to accidentally publish a million duplicates of your homepage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A 404 is a fine answer.&lt;/strong&gt; Do not redirect dead URLs somewhere polite.
The site this happened to is &lt;a href="https://toolore.com" rel="noopener noreferrer"&gt;Toolore&lt;/a&gt; — 78 calculators, converters and file tools that run entirely in the browser. The &lt;a href="https://toolore.com/guides" rel="noopener noreferrer"&gt;guides&lt;/a&gt; are new too.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you have inherited a domain and your pages are not getting indexed, open Search Console → Pages → the "Why pages aren't indexed" table, and look at the example URLs. If they are not URLs you wrote, you have the same problem I did — and possibly the same one-line fix.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>nextjs</category>
      <category>webdev</category>
      <category>googlesearchconsole</category>
    </item>
  </channel>
</rss>
