0% прочитано

Laravel robots.txt, sitemap і canonical: мій production SEO checklist

У ICanUp robots.txt, sitemap і canonical з'являлися не одночасно, але на production вони мають описувати один і той самий набір публічних URL. Я зібрала свій практичний SEO checklist: що дозволяємо сканувати, що додаємо в sitemap, який URL оголошуємо canonical і як перевіряю все це через raw HTML та HTTP-відповіді після deploy.

12 вересня 2026 р. 9 хв читанняЛаравель

У ICanUp robots.txt, sitemap.xml і canonical URL з'являлися на різних етапах розвитку SEO.

Кожна частина окремо виглядала простою. Але на production я зрозуміла, що перевіряти їх окремо недостатньо.

Усі ці механізми повинні описувати одну й ту саму реальність: які URL публічні, які можна сканувати, які варто знаходити через sitemap і який URL є основним для конкретної сторінки.

robots.txt, sitemap і canonical вирішують різні задачі. robots.txt керує crawling, sitemap допомагає знаходити публічні URL, а canonical описує основний URL конкретної сторінки.
Laravel production SEO contract connecting robots.txt, sitemap.xml, canonical URLs and public pages
Мій production SEO checklist починається з одного правила: robots.txt, sitemap і canonical мають спиратися на той самий контракт публічних URL.

Три механізми, три різні ролі

Найважливішою зміною в моєму підході стало те, що я перестала сприймати ці механізми як взаємозамінні.

Якщо sitemap містить URL, robots.txt випадково забороняє його сканування, а canonical вказує ще на іншу адресу, пошуковий робот отримує суперечливі сигнали.

Механізм

Основне завдання

Чого він не робить

robots.txt

Керує дозволом на crawling певних шляхів

Не визначає canonical URL і не є надійною заміною noindex

sitemap.xml

Перераховує URL, які застосунок вважає публічними та придатними для індексації

Не гарантує індексацію

canonical

Оголошує основну адресу поточної сторінки

Не закриває приватні маршрути

robots.txt у мене є динамічним endpoint

Я не хотіла тримати статичний файл, у якому production domain або sitemap URL можуть розійтися з реальною конфігурацією застосунку.

Тому /robots.txt формується застосунком і повертається як звичайна публічна відповідь text/plain.

Спрощений robots.txt
User-agent: *Disallow: /adminDisallow: /loginDisallow: /registerDisallow: /password Sitemap: https://icanup.com.ua/sitemap.xml

Конкретний список закритих шляхів змінюється разом із застосунком, але принцип залишається однаковим: публічний контент не повинен випадково потрапити під загальне Disallow.

robots.txt не є механізмом авторизації. Admin та інші приватні розділи мають залишатися захищеними самим застосунком. Так само Disallow не потрібно трактувати як гарантований noindex.

Sitemap містить тільки реально публічні URL

Мій sitemap починався з Post, але з розвитком ICanUp до нього додалися інші публічні сутності.

Для мене головний критерій тепер не тип моделі, а її фактична доступність для звичайного користувача і пошукового робота.

  • Публічні сторінки головної.
  • Активні публічні Category.
  • Опубліковані Post.
  • Опубліковані Page.
  • Активні публічні PostSeries.
  • Локалізовані URL лише для доступних мовних версій.

Чернетки, майбутні публікації до настання published_at, видалені та інші непублічні сутності в sitemap не повинні з'являтися.

Sitemap для мене є проєкцією public eligibility, а не просто списком рядків із таблиці.

XML - Спрощений sitemap із локалізованими URL
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">    <url>        <loc>            https://icanup.com.ua/posts/example-slug        </loc>        <lastmod>            2026-09-10T10:00:00+00:00        </lastmod>    </url>     <url>        <loc>            https://icanup.com.ua/en/posts/example-slug        </loc>        <lastmod>            2026-09-10T10:00:00+00:00        </lastmod>    </url></urlset>

Canonical відповідає на питання про identity сторінки

Sitemap говорить crawler, що URL існує і є важливим для обходу. Canonical має іншу роль.

На самій сторінці я явно вказую URL, який застосунок вважає основним для поточного контенту та поточної локалі.

HTML - Self-canonical для EN сторінки
<link    rel="canonical"    href="https://icanup.com.ua/en/posts/example-slug">

Для мультимовного Post EN canonical не повинен вести на українську сторінку тільки тому, що українська є default locale.

Кожна доступна мовна версія має власний self-canonical URL.

Цей контракт я детальніше розбирала у статті про canonical і hreflang для мультимовних сторінок Laravel.

Локалізація не повинна створювати різні правила для sitemap і canonical

У поточній архітектурі Post і Category мають один shared slug для всіх локалей.

Українська є default locale і не має префікса. Англійська використовує /en.

Тому URL повинні формуватися через ту саму locale-aware routing infrastructure, а не вручну в кожному SEO service.

Локаль

Публічний URL

Canonical

UK

/posts/example-slug

/posts/example-slug

EN

/en/posts/example-slug

/en/posts/example-slug

Це ж правило використовується під час формування sitemap, hreflang, Open Graph URL та інших SEO signals.

Public eligibility є спільним джерелом істини

Одна з найнебезпечніших помилок для мене - коли кожна SEO-підсистема окремо вирішує, чи є контент публічним.

Тоді sitemap може включити scheduled Post, public controller поверне для нього 404, а IndexNow уже надішле URL пошуковій системі.

SEO layer не повинен вигадувати власне визначення published content.

  • Draft не додається в sitemap.
  • Scheduled Post до часу публікації не додається в sitemap.
  • Soft-deleted content не додається.
  • Inactive Category або Series не рекламується як публічний URL.
  • Відсутня мовна версія не створює штучний localized URL.
  • Публічний URL повинен реально відкриватися за тим самим application contract.
Перед тим як додати новий тип контенту в sitemap, я спочатку перевіряю його public eligibility, routing і canonical contract. XML generation йде вже після цього.

lastmod повинен означати реальну зміну сторінки

Ще одна річ, яку я більше не перевіряю поверхнево, - lastmod.

Якщо Post має Content Blocks або локалізований контент, його публічна сторінка може змінитися без очевидної зміни основного posts.updated_at.

Тому значення lastmod має відображати останню релевантну зміну конкретної публічної версії сторінки.

Окремий реальний кейс із locale-specific Content Blocks я описала у статті про Laravel sitemap lastmod і ситуацію, коли updated_at моделі не є реальною датою зміни сторінки.

SEO я перевіряю в raw HTML, а не тільки в браузері

Після появи Inertia SSR для мене змінився спосіб production перевірки.

Тепер важливо не лише те, що я бачу після hydration у DevTools, а те, що отримує crawler одразу у відповіді сервера.

BASH - Перевірити SEO tags у початковому HTML
curl -s \  https://icanup.com.ua/en/posts/example-slug \  | grep -E \    'rel="canonical"|hreflang|name="robots"'

Це особливо корисно для canonical, hreflang, robots metadata, Open Graph та JSON-LD.

robots.txt і sitemap я перевіряю як HTTP endpoints

BASH - Перевірити SEO endpoints
curl -i \  https://icanup.com.ua/robots.txt curl -i \  https://icanup.com.ua/sitemap.xml
  • /robots.txt повертає HTTP 200.
  • /robots.txt має коректний text/plain Content-Type.
  • /sitemap.xml повертає HTTP 200.
  • Sitemap повертає XML Content-Type.
  • Обидва endpoints доступні без авторизації.
  • У robots.txt є актуальне посилання на sitemap.
  • Sitemap не містить admin або інших приватних URL.

XML потрібно перевіряти не тільки очима

Навіть якщо sitemap добре виглядає в браузері, помилка escaping або некоректна XML structure може зробити його непридатним для crawler.

BASH - Перевірити XML sitemap
curl -s \  https://icanup.com.ua/sitemap.xml \  > /tmp/icanup-sitemap.xml xmllint \  --noout \  /tmp/icanup-sitemap.xml
xmllint є лише одним із зручних локальних способів перевірки. Головна вимога - sitemap повинен залишатися валідним XML і проходити перевірку пошукових інструментів.

404 і sitemap не повинні суперечити один одному

Ще один простий production check - взяти кілька URL із sitemap і реально відкрити їх.

Якщо sitemap рекламує URL, який повертає 404, проблема вже не в XML. Проблема в тому, що два application contracts розійшлися.

BASH - Перевірити HTTP status public URL
curl -s \  -o /dev/null \  -w '%{http_code} %{url_effective}\n' \  https://icanup.com.ua/posts/example-slug

IndexNow доповнює sitemap, але не замінює його

Після підключення IndexNow у мене з'явився ще один indexing signal.

Але його роль інша: sitemap дає стабільний перелік публічних URL, а IndexNow повідомляє про конкретні зміни швидше.

Я не прибираю sitemap тільки тому, що application уже може submit URL через IndexNow.

Про цей pipeline я окремо писала у статті про автоматичну відправку нових та оновлених Laravel URL через IndexNow.

Сигнал

Що повідомляє

Sitemap

Ось набір публічних URL сайту

Canonical

Ось основний URL цієї конкретної сторінки

Hreflang

Ось доступні мовні версії цієї сторінки

IndexNow

Ось URL, стан якого щойно змінився

Мій production checklist після deploy

Після кількох SEO змін я перестала перевіряти тільки ту функцію, яку щойно змінювала.

Навіть невелика зміна routing або publication logic може вплинути одразу на кілька SEO signals.

  • Відкрити /robots.txt без авторизації.
  • Перевірити Content-Type robots.txt.
  • Перевірити актуальний Sitemap: URL.
  • Переконатися, що public routes не заблоковані випадковим Disallow.
  • Відкрити /sitemap.xml.
  • Перевірити HTTP 200 та XML Content-Type.
  • Перевірити валідність XML.
  • Знайти кілька реальних UK та EN URL у sitemap.
  • Переконатися, що draft і future content відсутні.
  • Перевірити lastmod після реальної зміни контенту.
  • Відкрити кілька URL із sitemap і перевірити HTTP 200.
  • Перевірити canonical у raw HTML.
  • Перевірити self-canonical окремо для UK та EN.
  • Перевірити hreflang і x-default.
  • Переконатися, що URL генерується через application routing.
  • Перевірити, що SSR віддає SEO metadata до виконання JavaScript.
  • Після значної SEO зміни перевірити Google Search Console і Bing Webmaster Tools.

Автоматизовані тести захищають контракт між цими частинами

Production checklist не замінює tests, а tests не замінюють production smoke check.

Для мене вони захищають різні рівні.

  • robots.txt повертає правильний status і Content-Type.
  • robots.txt містить sitemap URL.
  • sitemap повертає валідну XML response.
  • Published content присутній у sitemap.
  • Draft content відсутній.
  • Future-published content до часу публікації відсутній.
  • Inactive content не рекламується як indexable URL.
  • Localized URLs відповідають actual routes.
  • Canonical відповідає поточній locale.
  • SEO metadata присутні в SSR HTML.
  • Зміна Content Blocks коректно впливає на lastmod там, де це потрібно.

Помилки, які я тепер перевіряю першими

  • Вважати Disallow у robots.txt повною заміною noindex.
  • Додавати URL у sitemap лише тому, що record існує в database.
  • Додавати scheduled Post до sitemap до фактичної публікації.
  • Формувати locale URL вручну замість routing infrastructure.
  • Вести EN canonical на UK URL.
  • Вважати один updated_at достатнім для складної сторінки з Content Blocks.
  • Перевіряти canonical тільки після client-side hydration.
  • Не перевіряти HTTP status URL, які вже потрапили в sitemap.
  • Вважати IndexNow заміною sitemap.
  • Змінювати routing і не перевіряти всі залежні SEO signals.

Мій production SEO checklist не починається з Google. Він починається з того, чи сам застосунок послідовно описує свої публічні URL.

Що я залишила собі на майбутнє

  • robots.txt, sitemap і canonical мають різні ролі.
  • Public eligibility повинна бути спільною для SEO infrastructure.
  • Sitemap не повинен рекламувати URL, який застосунок сам повертає як 404.
  • Canonical формується через той самий locale-aware routing contract.
  • UK та EN мають власні self-canonical URL.
  • lastmod має відображати реальну зміну публічної сторінки.
  • SEO metadata потрібно перевіряти в raw SSR HTML.
  • SEO endpoints потрібно перевіряти як звичайні HTTP responses.
  • IndexNow доповнює sitemap, а не замінює його.
  • Після deploy потрібні і automated tests, і коротка production перевірка.

Висновок

Окремо реалізувати robots.txt, sitemap або canonical нескладно. Складніше зробити так, щоб вони не суперечили один одному після десятків наступних змін у routing, localization і publication logic.

У ICanUp я в результаті прийшла до простого правила: спочатку існує єдиний контракт публічного URL, а robots.txt, sitemap, canonical, hreflang та IndexNow лише описують різні його аспекти.

Коли всі ці сигнали походять з однієї реальності застосунку, production SEO стає значно легше перевіряти і значно важче випадково зламати.