Другое

Next.js robots.txt не найден: Полное руководство по решению

Исправьте ошибки 404 robots.txt в Next.js с помощью нашего подробного руководства. Узнайте, почему /robots.txt перенаправляет на страницу 'Не найдено', а /robots работает, и реализуйте надежные решения с помощью промежуточного ПО, статической генерации или настройки маршрутов.

Почему мой файл robots.txt в Next.js перенаправляет на страницу “Not Found” при доступе по адресу http://localhost:3000/robots.txt, но при этом http://localhost:3000/robots работает корректно? Я пытаюсь динамически генерировать файл robots.txt с помощью API-маршрута в моем приложении Next.js.

Вот мой файл robots.ts в папке API:

typescript
import type { NextApiRequest, NextApiResponse } from 'next';
import { GraphQLRobotsService } from '@sitecore-jss/sitecore-jss-nextjs';
import { siteResolver } from 'lib/site-resolver';
import clientFactory from 'lib/graphql-client-factory';

const robotsApi = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
  res.setHeader('Content-Type', 'text/plain');

  // Resolve site based on hostname
  const hostName = req.headers['host']?.split(':')[0] || 'localhost';
  const site = siteResolver.getByHost(hostName);

  // create robots graphql service
  const robotsService = new GraphQLRobotsService({
    clientFactory,
    siteName: site.name,
  });

  const robotsResult = await robotsService.fetchRobots();

  return res.status(200).send(robotsResult);
};

export default robotsApi;

А вот мой файл next.config.js с конфигурацией перезаписей:

javascript
const jssConfig = require('./src/temp/config');
const plugins = require('./src/temp/next-config-plugins') || {};
const path = require('path');

const publicUrl = jssConfig.publicUrl;

/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  // Set assetPrefix to our public URL
  assetPrefix: publicUrl,

  // Allow specifying a distinct distDir when concurrently running app in a container
  distDir: process.env.NEXTJS_DIST_DIR || '.next',

  // Make the same PUBLIC_URL available as an environment variable on the client bundle
  env: {
    PUBLIC_URL: publicUrl,
  },

  i18n: {
    // These are all the locales you want to support in your application.
    // These should generally match (or at least be a subset of) those in Sitecore.
    locales: ['ms', 'en', 'ms-MY'],
    // This is the locale that will be used when visiting a non-locale
    // prefixed path e.g. `/styleguide`.
    defaultLocale: jssConfig.defaultLanguage,
    localeDetection: false,
  },

  // Enable React Strict Mode
  reactStrictMode: true,

  // Disable the X-Powered-By header. Follows security best practices.
  poweredByHeader: false,

  // use this configuration to ensure that only images from the whitelisted domains
  // can be served from the Next.js Image Optimization API
  // see https://nextjs.org/docs/app/api-reference/components/image#remotepatterns
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'edge*.**',
        port: '',
      },
      {
        protocol: 'https',
        hostname: 'xmc-*.**',
        port: '',
      },
      {
        protocol: 'https',
        hostname: 'feaas*.blob.core.windows.net',
        port: '',
      },
    ]
  },

  async rewrites() {
    // When in connected mode we want to proxy Sitecore paths off to Sitecore
    return [
      {
        // When I change /robots.txt to /robots, http://localhost:3000/robots starts to work.
        source: '/robots.txt',
        destination: '/api/robots',
      },
      // ... other rewrites
    ];
  },
  // ... other configurations
};

module.exports = () => {
  // Run the base config through any configured plugins
  return Object.values(plugins).reduce((acc, plugin) => plugin(acc), nextConfig);
};

Я также пробовал создать плагин для обработки перезаписи robots.txt, но это все еще не работает. Вот мой файл src/lib/next-config/plugins/robots.js:

javascript
/**
 * @param {import('next').NextConfig} nextConfig
 */
const robotsPlugin = (nextConfig = {}) => {
  return Object.assign({}, nextConfig, {
    async rewrites() {
      const baseRewrites = (await nextConfig.rewrites?.()) || {
        beforeFiles: [],
        afterFiles: [],
        fallback: [],
      };

      baseRewrites.beforeFiles.push({
        source: '/robots.txt',
        destination: '/api/robots',
      });

      return baseRewrites;
    },
  });
};

module.exports = robotsPlugin;

Как правильно настроить Next.js для обслуживания файла robots.txt из моего API-маршрута по адресу http://localhost:3000/robots.txt?

Проблема, с которой вы сталкиваетесь с перенаправлением robots.txt в Next.js на страницу “Not Found”, является распространенным конфликтом маршрутизации, который возникает, когда встроенная обработка robots.txt в Next.js мешает вашей перезаписи API-маршрута. На основе результатов исследования это происходит потому, что App Router в Next.js имеет специальную обработку файлов robots.txt, которая может переопределить вашу конфигурацию перезаписи.

Основная причина, как выяснилось, представляет собой конфликт маршрутизации между встроенной обработкой robots.txt в Next.js и вашей перезаписью API-маршрута. При доступе к /robots.txt App Router в Next.js сначала ищет статический файл robots.txt, и только если его не находит, рассматривает перезаписи. Это объясняет, почему /robots работает (это стандартный API-маршрут), а /robots.txt возвращает 404.


Содержание


Понимание конфликта маршрутизации

Как упоминается в GitHub issue #47198, проблема вызвана конфликтами маршрутов. App Router в Next.js имеет встроенную обработку файлов robots.txt и sitemap.xml, которая имеет приоритет над перезаписями в определенных сценариях.

Официальная документация показывает, что Next.js ожидает найти файлы robots.txt в определенных местах, и когда он не может найти их в ожидаемых местах, он может не правильно вернуться к вашим правилам перезаписи.

Ключевое из исследования заключается в том, что переписывания для robots.txt в App Router работают ненадежно из-за этой встроенной обработки. Разработчики сообщали об этой проблеме в различных версиях Next.js.


Решение 1: Использование промежуточного ПО для обработки robots.txt

Наиболее надежное решение - использовать промежуточное ПО (middleware) в Next.js для перехвата запросов /robots.txt и перенаправления их на ваш API-маршрут:

Создайте файл middleware.ts в корне вашего проекта:

typescript
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname === '/robots.txt') {
    // Переписываем запросы robots.txt на ваш API-маршрут
    return NextResponse.rewrite(new URL('/api/robots', request.url));
  }
  
  return NextResponse.next();
}

export const config = {
  matcher: [
    // Соответствовать всем путям запросов, кроме начинающихся с:
    // - api (API-маршруты)
    // - _next/static (статические файлы)
    // - _next/image (файлы оптимизации изображений)
    // - favicon.ico (файл favicon)
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

Этот подход работает, потому что промежуточное ПО выполняется до любой логики маршрутизации, обеспечивая приоритет вашей перезаписи над встроенной обработкой Next.js.


Решение 2: Статический robots.txt с динамическим содержимым

Другой подход - создать статический файл robots.txt, но сделать его содержимое динамическим. Вы можете создать скрипт, который генерирует содержимое robots.txt и записывает его в общедоступный каталог во время сборки:

javascript
// scripts/generate-robots.js
const fs = require('fs');
const path = require('path');

// Импортируйте ваш сервис robots
const { GraphQLRobotsService } = require('@sitecore-jss/sitecore-jss-nextjs');
const { siteResolver } = require('lib/site-resolver');
const clientFactory = require('lib/graphql-client-factory');

async function generateRobots() {
  const hostName = 'localhost'; // Возможно, вы захотите сделать это настраиваемым
  const site = siteResolver.getByHost(hostName);
  
  const robotsService = new GraphQLRobotsService({
    clientFactory,
    siteName: site.name,
  });
  
  const robotsResult = await robotsService.fetchRobots();
  
  // Запись в общедоступный каталог
  const robotsPath = path.join(process.cwd(), 'public', 'robots.txt');
  fs.writeFileSync(robotsPath, robotsResult);
  
  console.log('robots.txt успешно сгенерирован');
}

generateRobots().catch(console.error);

Добавьте этот скрипт в ваш package.json:

json
{
  "scripts": {
    "generate-robots": "node scripts/generate-robots.js"
  }
}

Затем запустите его перед сборкой:

bash
npm run generate-robots
npm run build

Этот подход гарантирует наличие статического файла robots.txt, который поисковые системы могут найти надежно.


Решение 3: Группы маршрутов и структура файлов

Согласно результатам исследования из блога Дариуса МакФарланда, группы маршрутов могут влиять на маршрутизацию robots.txt.

Попробуйте организовать ваши файлы следующим образом:

app/
├── api/
│   └── robots/
│       └── route.ts  # Ваш обработчик API
├── robots.txt.ts      # Файл метаданных для robots.txt
└── layout.tsx

В robots.txt.ts:

typescript
import type { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  // Это будет автоматически обслуживаться по адресу /robots.txt
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
      },
    ],
    sitemap: 'https://your-site.com/sitemap.xml',
  };
}

И измените ваш API-маршрут для обработки фактической генерации динамического содержимого.


Решение 4: Отключение встроенной обработки robots.txt в Next.js

Если вы хотите полностью контролировать robots.txt через ваш API-маршрут, вы можете попробовать отключить встроенную обработку Next.js. Это более сложный подход и может не быть рекомендованным, но это вариант:

javascript
// next.config.js
const nextConfig = {
  // ... другая конфигурация
  
  async rewrites() {
    return [
      {
        source: '/robots.txt',
        destination: '/api/robots',
      },
    ];
  },
  
  // Добавьте экспериментальную конфигурацию
  experimental: {
    // Это может помочь с конфликтами маршрутизации
    serverComponentsExternalPackages: ['@sitecore-jss/sitecore-jss-nextjs'],
  },
};

Однако, как отмечено в GitHub issues, этот подход может работать ненадежно из-за лежащих в основе конфликтов маршрутизации.


Лучшие практики для robots.txt в Next.js

На основе результатов исследования, вот некоторые лучшие практики:

  1. Используйте промежуточное ПО для динамического robots.txt: Это наиболее надежный подход для проектов с App Router.

  2. Рассмотрите статическую генерацию для производства: Если содержимое вашего robots.txt меняется нечасто, генерируйте его во время сборки.

  3. Тщательно тестируйте: Как упоминается в обсуждении на Reddit, разные среды (localhost против production) могут вести себя по-разному.

  4. Проверяйте на конфликты маршрутизации: Убедитесь, что у вас нет конфликтующих обработчиков маршрутов или промежуточного ПО, которые могут мешать.

  5. Следите за обновлениями версий Next.js: Проблема была зарегистрирована в различных версиях Next.js, и исправления могут быть доступны в более новых выпусках.


Шаги по устранению неполадок

Если ни одно из вышеперечисленных решений не работает, попробуйте следующие шаги по устранению неполадок:

  1. Проверьте на наличие конфликтующих файлов: Ищите любые существующие файлы robots.txt в вашем каталоге public или в других местах вашего проекта.

  2. Очистите кэш Next.js: Иногда очистка каталога .next может решить проблемы с маршрутизацией.

  3. Протестируйте с минимальной конфигурацией: Создайте минимальный проект Next.js только с перезаписью robots.txt, чтобы изолировать проблему.

  4. Проверьте порядок промежуточного ПО: Если у вас несколько файлов промежуточного ПО, убедитесь, что они не мешают друг другу.

  5. Проверьте заголовки запросов: Убедитесь, что ваш API-маршрут правильно обрабатывает заголовок Content-Type как text/plain.

  6. Тестируйте в разных средах: Проблема может быть специфичной для среды, поэтому тестируйте как в разработке, так и в производстве.

Ключевой вывод из исследования заключается в том, что переписывания для robots.txt в App Router Next.js могут быть ненадежными из-за встроенной обработки, и решения на основе промежуточного ПО или статической генерации часто являются более надежными подходами.

Авторы
Проверено модерацией
Модерация