У ICanUp robots.txt, sitemap.xml і canonical URL з'являлися на різних етапах розвитку SEO.
Кожна частина окремо виглядала простою. Але на production я зрозуміла, що перевіряти їх окремо недостатньо.
Усі ці механізми повинні описувати одну й ту саму реальність: які URL публічні, які можна сканувати, які варто знаходити через sitemap і який URL є основним для конкретної сторінки.

Три механізми, три різні ролі
Найважливішою зміною в моєму підході стало те, що я перестала сприймати ці механізми як взаємозамінні.
Якщо sitemap містить URL, robots.txt випадково забороняє його сканування, а canonical вказує ще на іншу адресу, пошуковий робот отримує суперечливі сигнали.
Механізм | Основне завдання | Чого він не робить |
|---|---|---|
| Керує дозволом на crawling певних шляхів | Не визначає canonical URL і не є надійною заміною noindex |
| Перераховує URL, які застосунок вважає публічними та придатними для індексації | Не гарантує індексацію |
| Оголошує основну адресу поточної сторінки | Не закриває приватні маршрути |
robots.txt у мене є динамічним endpoint
Я не хотіла тримати статичний файл, у якому production domain або sitemap URL можуть розійтися з реальною конфігурацією застосунку.
Тому /robots.txt формується застосунком і повертається як звичайна публічна відповідь text/plain.
User-agent: *Disallow: /adminDisallow: /loginDisallow: /registerDisallow: /password Sitemap: https://icanup.com.ua/sitemap.xmlКонкретний список закритих шляхів змінюється разом із застосунком, але принцип залишається однаковим: публічний контент не повинен випадково потрапити під загальне Disallow.
Sitemap містить тільки реально публічні URL
Мій sitemap починався з Post, але з розвитком ICanUp до нього додалися інші публічні сутності.
Для мене головний критерій тепер не тип моделі, а її фактична доступність для звичайного користувача і пошукового робота.
- Публічні сторінки головної.
- Активні публічні Category.
- Опубліковані Post.
- Опубліковані Page.
- Активні публічні PostSeries.
- Локалізовані URL лише для доступних мовних версій.
Чернетки, майбутні публікації до настання published_at, видалені та інші непублічні сутності в sitemap не повинні з'являтися.
Sitemap для мене є проєкцією public eligibility, а не просто списком рядків із таблиці.
<?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, який застосунок вважає основним для поточного контенту та поточної локалі.
<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 |
|
|
EN |
|
|
Це ж правило використовується під час формування 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.
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 одразу у відповіді сервера.
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
curl -i \ https://icanup.com.ua/robots.txt curl -i \ https://icanup.com.ua/sitemap.xml/robots.txtповертає HTTP 200./robots.txtмає коректнийtext/plainContent-Type./sitemap.xmlповертає HTTP 200.- Sitemap повертає XML Content-Type.
- Обидва endpoints доступні без авторизації.
- У robots.txt є актуальне посилання на sitemap.
- Sitemap не містить admin або інших приватних URL.
XML потрібно перевіряти не тільки очима
Навіть якщо sitemap добре виглядає в браузері, помилка escaping або некоректна XML structure може зробити його непридатним для crawler.
curl -s \ https://icanup.com.ua/sitemap.xml \ > /tmp/icanup-sitemap.xml xmllint \ --noout \ /tmp/icanup-sitemap.xml404 і sitemap не повинні суперечити один одному
Ще один простий production check - взяти кілька URL із sitemap і реально відкрити їх.
Якщо sitemap рекламує URL, який повертає 404, проблема вже не в XML. Проблема в тому, що два application contracts розійшлися.
curl -s \ -o /dev/null \ -w '%{http_code} %{url_effective}\n' \ https://icanup.com.ua/posts/example-slugIndexNow доповнює 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 стає значно легше перевіряти і значно важче випадково зламати.



