Skip to main content
Назад до блогу
3 хв читання

vercel.json, що мовчки зламав мій сайтмап на місяць

Одне правило редиректу в vercel.json змусило сайтмап повертати HTML замість XML. Google перестав індексувати нові сторінки на 4 тижні. Як я знайшов і зневадив.

performancenext.js

Чотири тижні Google не індексував жодної нової сторінки на сайті клієнта. Ні помилок у Search Console. Ні проблем із краулінгом. Сайтмап показував статус «успішно». Я не помітив, доки не пошукав блог-пост за точною назвою і не отримав нуль результатів.

Коренева причина — один рядок у vercel.json.

Симптом

Я опублікував 3 нових блог-пости за місяць. Жоден не з'явився в Google. Старі пости продовжували ранжуватися нормально. Сайт був здоровий — ні 500 помилок, ні проблем із деплоєм, швидке завантаження.

Я перевірив Google Search Console:

Останній рядок був підказкою. Google закраулив сайтмап місяць тому і не повернувся. Але статус сайтмапу казав «успішно». Чому б Google перестав рекраулити сайтмап, який він успішно обробив?

Розслідування

Крок 1: Я відкрив URL сайтмапу в браузері.

https://client-site.com/sitemap.xml

Відрендерилася головна сторінка. Не XML — справжня HTML-головна.

Крок 2: Перевірив curl-ом, щоб виключити браузерні редиректи.

curl -v https://client-site.com/sitemap.xml
< HTTP/2 200
< content-type: text/html; charset=utf-8

URL сайтмапу повернув 200 з text/html. Сайтмап віддавався як HTML-сторінка. Google «успішно» його забрав, побачив HTML і мовчки перестав намагатися парсити як сайтмап.

Крок 3: Я перевірив vercel.json.

Блок rewrites виключав sitemap з перезапису URL. Але блок redirects — ні. Редиректи виконуються до реврайтів. /sitemap.xml перехоплювався редиректом і перенаправлявся на /en/sitemap.xml.

/en/sitemap.xml не існувало. Next.js рендерив catch-all 404 (який повертав 200 через окрему проблему — неправильно налаштовану not-found сторінку). Google бачив 200 HTML-відповідь і позначав сайтмап як «оброблений».

Фікс

Дві зміни:

  1. Додав sitemap та robots у regex-виключення редиректів (не тільки реврайтів)
  2. Виправив not-found сторінку, щоб реально повертала 404-статус

Після деплою:

curl -I https://client-site.com/sitemap.xml
HTTP/2 200
content-type: application/xml

Коректно. XML-сайтмап віддається безпосередньо з Next.js app/sitemap.ts.

Чому не зловив раніше

  1. Vercel деплоїть мовчки. Немає валідації vercel.json проти твоїх реальних роутів. Правило редиректу було технічно валідним — просто матчило більше URL, ніж планувалося.

  2. Google Search Console сказав «успішно». 200-відповідь з HTML — не помилка для процесора сайтмапів Google. Він просто мовчки ігнорує не-XML контент. Ні попередження, ні алерту.

  3. Старі сторінки продовжували ранжуватися. Google вже проіндексував старий контент. Невидимим був тільки новий — а це помічаєш через тижні.

  4. Локальна розробка не використовує vercel.json. next dev ігнорує Vercel-специфічні правила редиректів. Сайтмап працював ідеально локально.

Чекліст запобігання

Після цього інциденту я додав у деплой-пайплайн:

# Smoke-тест після деплою
SITEMAP_TYPE=$(curl -s -o /dev/null -w "%{content_type}" https://yoursite.com/sitemap.xml)
if [[ "$SITEMAP_TYPE" != *"xml"* ]]; then
  echo "ALERT: Sitemap is not returning XML"
  exit 1
fi

ROBOTS_TYPE=$(curl -s -o /dev/null -w "%{content_type}" https://yoursite.com/robots.txt)
if [[ "$ROBOTS_TYPE" != *"text/plain"* ]]; then
  echo "ALERT: robots.txt is not returning text/plain"
  exit 1
fi

Дві перевірки curl. 10 секунд на виконання. Зловили б це на першому деплої, а не через 4 тижні.

Мета-урок

Редиректи та реврайти vercel.json обробляються в порядку: headers → redirects → rewrites → файлова система. Якщо редирект матчиться до виключення реврайту — виключення ніколи не запускається. А якщо твій catch-all роут повертає 200 замість 404, кожен зламаний редирект стає невидимим — виглядає робочим, бо код відповіді каже OK.

Тестуй інфраструктурні роути (/sitemap.xml, /robots.txt, /api/*) після кожної зміни vercel.json. Не тільки роути застосунку.


Турбуєшся про тихі SEO-проблеми на своєму Next.js-сайті? Напиши — я запускаю пост-деплой перевірки на кожному клієнтському проєкті саме для того, щоб ловити цей клас невидимих багів.