<?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: Eugene</title>
    <description>The latest articles on DEV Community by Eugene (@numart).</description>
    <link>https://dev.to/numart</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%2F1890960%2F3d7830be-51c1-4206-a6e6-c36e70b22279.png</url>
      <title>DEV Community: Eugene</title>
      <link>https://dev.to/numart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/numart"/>
    <language>en</language>
    <item>
      <title>How a login wall hid 600 pages from Google</title>
      <dc:creator>Eugene</dc:creator>
      <pubDate>Wed, 12 Aug 2026 19:21:21 +0000</pubDate>
      <link>https://dev.to/numart/how-a-login-wall-hid-600-pages-from-google-2h9c</link>
      <guid>https://dev.to/numart/how-a-login-wall-hid-600-pages-from-google-2h9c</guid>
      <description>&lt;p&gt;podbor-minuta.ru has about 600 listing pages for apartments, grouped by metro, district, room count and price. They are built ahead of time into ready HTML so search engines can read them without running JavaScript. Each has its own title, description and a real list of apartments.&lt;/p&gt;

&lt;p&gt;In Google Search Console these pages were marked as indexed. Over 90 days they got zero impressions. Zero. Other sections, building cards and articles, did get impressions.&lt;/p&gt;

&lt;p&gt;I chased the wrong ideas first: thin content? crawl budget on a young domain? duplicates? The titles and descriptions were fine and the pages were in the index. So I looked at what search engines actually receive - the ready HTML.&lt;/p&gt;

&lt;p&gt;I fetched a page and stripped the head and scripts to see only the visible text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://podbor-minuta.ru/novostroyki/metro/domodedovskaya/do-10-mln &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/&amp;lt;script.*script&amp;gt;//g'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of a list of apartments there was one thing: "Log in with Telegram to use the service." A sign-in form. It went straight into the ready HTML. No page title, no list, just a prompt to sign in.&lt;/p&gt;

&lt;p&gt;The reason is how rendering works. All content is wrapped in an access check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthGate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AuthGate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When pages are built ahead of time there is no user and no session. For pages treated as non-public, the check shows the sign-in form. That form is what gets saved to the file. The title and description are prepared separately, so they stayed correct. That is why the pages looked healthy in the index while the search engine read the sign-in form as their content.&lt;/p&gt;

&lt;p&gt;The check itself is a list of public paths:&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;PUBLIC_PATH_PREFIXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/project/&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="s1"&gt;/articles/&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="s1"&gt;/discounts/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getRouteTier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC_PATHS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC_PATH_PREFIXES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authenticated&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The paths /novostroyki/ and /developer/ were never added. An unknown path is treated as closed, which means the sign-in form.&lt;/p&gt;

&lt;p&gt;The fix is two lines:&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;PUBLIC_PATH_PREFIXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/project/&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="s1"&gt;/articles/&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="s1"&gt;/discounts/&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="s1"&gt;/novostroyki/&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="s1"&gt;/developer/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a rebuild all these pages return the real title, intro and apartment list instead of the sign-in form.&lt;/p&gt;

&lt;p&gt;What I took from this.&lt;/p&gt;

&lt;p&gt;Prebuilt pages inherit your access check. They are built as a guest. What a logged-out visitor sees is what gets frozen into the file, and that is what the search engine reads.&lt;/p&gt;

&lt;p&gt;If a whole section gets zero impressions, it is almost always technical, not content. One page low in results is competition. A whole pattern at zero - look at the ready HTML, not the page in your browser where you are logged in and everything looks fine.&lt;/p&gt;

&lt;p&gt;The title and the body can disagree. A correct title and description can hide a broken body.&lt;/p&gt;

&lt;p&gt;The reliable guard is not the list entry but a build-time check: no prebuilt page should contain the sign-in form. Then this kind of bug cannot ship silently.&lt;/p&gt;

&lt;p&gt;600 pages were invisible to search for months. The fix was two lines. The expensive part was trusting the titles instead of looking at the page.&lt;/p&gt;

&lt;p&gt;The site is podbor-minuta.ru, daily price monitoring for Moscow new builds.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>react</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Как логин-стена спрятала 600 страниц от Google</title>
      <dc:creator>Eugene</dc:creator>
      <pubDate>Wed, 12 Aug 2026 17:49:58 +0000</pubDate>
      <link>https://dev.to/numart/the-auth-gate-that-hid-600-pages-from-google-3g7n</link>
      <guid>https://dev.to/numart/the-auth-gate-that-hid-600-pages-from-google-3g7n</guid>
      <description>&lt;p&gt;У нас на сайте podbor-minuta.ru около 600 страниц-подборок квартир: по метро, району, числу комнат и цене. Они собираются заранее в готовый HTML, чтобы поисковики видели их без запуска JavaScript. У каждой свой заголовок, описание и реальный список квартир.&lt;/p&gt;

&lt;p&gt;В Google Search Console эти страницы числились проиндексированными. За 90 дней они не получили ни одного показа. Ноль. При этом другие разделы, карточки жилых комплексов и статьи, показы получали.&lt;/p&gt;

&lt;p&gt;Сначала я думал не туда: мало контента? маленький бюджет обхода у молодого домена? дубли? Заголовки и описания были в порядке, страницы в индексе. Тогда я посмотрел на то, что на самом деле уходит поисковику - на готовый HTML.&lt;/p&gt;

&lt;p&gt;Я скачал страницу и убрал из неё head и скрипты, чтобы увидеть только видимый текст:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://podbor-minuta.ru/novostroyki/metro/domodedovskaya/do-10-mln &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/&amp;lt;script.*script&amp;gt;//g'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Вместо списка квартир там было одно: "Войдите через Telegram, чтобы пользоваться сервисом". Форма входа. Она попала прямо в готовый HTML. Ни заголовка страницы, ни списка, только предложение авторизоваться.&lt;/p&gt;

&lt;p&gt;Причина в том, как устроен показ. Весь контент оборачивается в проверку доступа:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthGate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AuthGate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Когда страницы собираются заранее, пользователя нет и сессии нет. Для страниц, которые считаются не публичными, проверка показывает форму входа. И именно она сохраняется в файл. А заголовок и описание готовятся отдельно, поэтому остались правильными. Из-за этого страницы выглядели здоровыми в индексе, но поисковик читал как их содержимое форму входа.&lt;/p&gt;

&lt;p&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PUBLIC_PATH_PREFIXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/project/&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="s1"&gt;/articles/&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="s1"&gt;/discounts/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getRouteTier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC_PATHS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC_PATH_PREFIXES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authenticated&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Пути /novostroyki/ и /developer/ в список не добавили. Незнакомый путь считается закрытым, а значит показывается форма входа.&lt;/p&gt;

&lt;p&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PUBLIC_PATH_PREFIXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/project/&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="s1"&gt;/articles/&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="s1"&gt;/discounts/&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="s1"&gt;/novostroyki/&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="s1"&gt;/developer/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;После пересборки все эти страницы стали отдавать реальный заголовок, вводный текст и список квартир вместо формы входа.&lt;/p&gt;

&lt;p&gt;Что я вынес.&lt;/p&gt;

&lt;p&gt;Готовые страницы наследуют вашу проверку доступа. Их собирают от лица гостя. Что видит незалогиненный посетитель, то и замораживается в файл, и то же читает поисковик.&lt;/p&gt;

&lt;p&gt;Если целый раздел получает ноль показов, это почти всегда техническая причина, а не контент. Одна страница внизу выдачи - это конкуренция. Весь шаблон на нуле - смотрите готовый HTML, а не страницу в браузере, где вы залогинены и всё выглядит нормально.&lt;/p&gt;

&lt;p&gt;Заголовок и тело страницы могут расходиться. Правильные заголовок и описание могут прятать сломанное тело.&lt;/p&gt;

&lt;p&gt;Надёжная защита - не запись в списке, а проверка на сборке: ни одна готовая страница не должна содержать форму входа. Тогда такая ошибка не пройдёт незаметно.&lt;/p&gt;

&lt;p&gt;600 страниц были невидимы для поиска несколько месяцев. Починка заняла две строки. Дорого обошлось то, что я верил заголовкам вместо того, чтобы посмотреть на саму страницу.&lt;/p&gt;

&lt;p&gt;Сайт - podbor-minuta.ru, ежедневный мониторинг цен на новостройки Москвы.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>react</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
