Sitemap Next.js возвращает 404: четыре проверки в dev и production
Neaptide · 13 октября 2026 г. · 7 мин чтения
Эксперимент на Next.js 15.5.22: неверный адрес sitemap, сбой источника данных и 404 внутри корректного XML. Исправления, исходные результаты и стенд для повторения.
В этой статье

Прежде чем менять настройки sitemap в Next.js, выясните, какой адрес вернул ошибку. Сама карта сайта, источник данных для её генерации и URL товара внутри XML могут ломаться независимо. Один скриншот страницы 404 не показывает, где нужно исправление.
12 октября 2026 года Neaptide проверил четыре сценария на локальном стенде. Неверный адрес sitemap вернул 404; недоступный источник — 500 на адресе карты, поскольку наш обработчик явно выбрасывал исключение; карта со снятым с публикации товаром вернула 200, а сам товар — 404. Исправления сработали и в next dev, и в локальной production-сборке. Различий между режимами в этих сценариях мы не обнаружили.
Что проверили до написания статьи
Стенд использует Next.js 15.5.22, React и React DOM 19.2.6, Node v25.2.1 и два вымышленных товара. До замеров мы зафиксировали протокол, код, сборщик данных и lock-файл, установили зависимости в отдельном каталоге и запустили next dev одновременно с next build и next start. Записи товаров отдаёт локальный HTTP-источник. Production-сборка работает локально; это не тест Vercel.
Четыре сценария дают семь вариантов: исправный контрольный и три пары до/после. Два режима и два прогона — 28 ответов sitemap. Все URL из XML проверены отдельно: ещё 36 ответов страниц и две проверки источника. Всего 66 повторных HTTP-наблюдений, а не 66 уникальных URL. Сохранены тела, заголовки и хеши. Проверка XML включает разбор и пространство имён urlset, но не полную валидацию по XSD.
Одного статуса sitemap оказалось недостаточно

| Сценарий | Состояние | HTTP sitemap | URL в XML | HTTP страниц |
|---|---|---|---|---|
| Статический контроль | Контроль | 200 | 2 | 200 / 200 |
| Источник данных | До | 500 | — | — |
| Источник данных | После | 200 | 2 | 200 / 200 |
| Адрес карты | До | 404 | — | — |
| Адрес карты | После | 200 | 2 | 200 / 200 |
| Снятый товар | До | 200 | 2 | 200 / 404 |
| Снятый товар | После | 200 | 1 | 200 |
404 источника привёл к 500 в нашем обработчике
Динамическая карта запрашивает локальный JSON. До исправления путь /missing-catalog.json возвращал 404. Обработчик ниже проверяет response.ok и выбрасывает исключение. Поэтому /dynamic-bad/sitemap.xml вернул 500. Результат зависит от нашей обработки ошибки: это не общее правило преобразования 404 в 500 в Next.js.
// lib/catalog.js
export async function fromFeed(path) {
const response=await fetch(`http://127.0.0.1:8800${path}`,{cache:'no-store'});
if(!response.ok) throw new Error(`Controlled catalog source returned HTTP ${response.status}`);
const rows=await response.json();return entries(rows.map(row=>row.slug));
}Достаточно было заменить путь источника на /catalog.json: /dynamic-ok/sitemap.xml вернул 200 и два доступных товара. Оба варианта уже использовали force-dynamic и cache: 'no-store'. Добавление динамического рендеринга не было проверенным исправлением. В своём приложении сначала сохраните статус источника и изучите серверный журнал, затем решайте, менять ли кеширование.
// app/dynamic-ok/sitemap.js
import {fromFeed} from '../../lib/catalog';
export const dynamic='force-dynamic';
export default async function sitemap(){return fromFeed('/catalog.json');}Сопоставьте адрес с расположением файла
Файл app/catalog/sitemap.js создал маршрут /catalog/sitemap.xml. Запрос /sitemaps/catalog.xml вернул 404. Запрос настоящего адреса дал 200 без изменений кода. Контрольный app/sitemap.js в корне также вернул 200. Эти пути соответствуют файловой конвенции Next.js. Разбитые карты с generateSitemaps мы не проверяли.
app/sitemap.js → /sitemap.xml
app/catalog/sitemap.js → /catalog/sitemap.xmlКарта с кодом 200 может содержать страницу 404
Вариант stale-before включал все редакционные записи, в том числе /products/retired. XML разбирался, карта возвращала 200, но снятый товар — 404. Фильтр published перед генерацией URL оставил одну запись, /products/widget-a, со статусом 200. Мы исключили неопубликованный товар, не создавая редирект и не подменяя его другим.
// app/stale-after/sitemap.js
import {editorialRecords,entries} from '../../lib/catalog';
export default function sitemap(){return entries(editorialRecords.filter(row=>row.published).map(row=>row.slug));}Флаг published сам по себе не определяет индексируемость. В реальном каталоге также проверьте, что выбраны нужные канонические страницы и они допускают индексацию. Страницы стенда намеренно содержат noindex/nofollow: здесь измеряется доступность маршрутов.
Проследите, на каком адресе возникает ошибка
Начните с точного адреса отправленной карты и GET-запроса. Сохраните статус и тело до перехода по редиректам. Схема ниже — практический порядок диагностики; некорректный XML и сбои только на хостинге указаны как рекомендации, а не дополнительные измеренные сценарии.

- Sitemap возвращает 404: сравните запрошенный путь с фактическим маршрутом и файлом. В S3 помогло исправление адреса.
- Sitemap возвращает 5xx: изучите серверную ошибку маршрута и ответ источника. В S2 отсутствующий источник вызывал исключение; исправление его пути устранило сбой.
- Sitemap возвращает 200: разберите тело и проверьте пространство имён корневого элемента. Если пришёл HTML или некорректный XML, исследуйте маршрут, формирующий ответ. Такой сбой мы не моделировали.
- XML разбирается: запросите адреса loc и проверьте статусы. В S4 это выявило снятый товар, который проверка одной карты пропустила.
- Сбой возникает только на хостинге: сравните коммит, путь запроса, переменные окружения и доступность источника с next build и next start. Затем изучите маршрутизацию и журналы деплоя. Наш локальный тест не выявлял ошибок хостинга.
Google рекомендует абсолютные адреса предпочтительных страниц, которые вы хотите видеть в поиске. Отправка карты служит подсказкой и не гарантирует обход. После деплоя проверьте ошибки обработки в отчёте Sitemaps в Search Console: успешный локальный ответ не показывает результат Google.
Повторите проверку на том же стенде
Скачайте архив и следуйте README.txt. Запустите локальный источник, установите закреплённые зависимости и поднимите dev и production на портах 8801 и 8802. Сборщик сохраняет всю матрицу без перехода по редиректам и обхода внешних сайтов. Архив сопоставлен с зафиксированным кодом; повторный запуск его сборщика на исходных работающих серверах подтвердил все 28 результатов sitemap. Это не вторая чистая установка серверов.
Методика, данные и рабочий пример
- Результаты и наблюдения · JSON
- Методика и ограничения · TXT
- Стенд, lock-файл и сборщик · ZIP
- Исходные HTTP-ответы и журналы · ZIP
Ошибки внесены намеренно и помогают разобрать диагностику, но не показывают распространённость проблем. Мы не тестировали живую CMS, сброс кеша, middleware, rewrites, CDN или Vercel и не измеряли индексацию, позиции или цитирование ИИ. Историческое обращение по Next.js RC/Windows/Prisma помогло сформулировать вопрос; этот стенд не воспроизводит и не решает проблему той среды.