\n \n )}\n \n \n {isBot ? (\n // Полная версия для роботов\n
\n {questions.map(q => (\n
\n

{q.title}

\n

{q.description}

\n {q.category}\n
\n ))}\n
\n ) : (\n // Компактная версия для пользователей\n \n )}\n
\n );\n}\n\nexport async function getServerSideProps({ req, res }) {\n const isBot = detectBot(req.headers[\"user-agent\"]);\n \n // Увеличиваем кэш для робота запросов\n if (isBot) {\n res.setHeader(\"Cache-Control\", \"public, s-maxage=3600, stale-while-revalidate=86400\");\n }\n \n const questions = await getQuestionsFromDB({\n limit: isBot ? 100 : 10\n });\n \n return {\n props: {\n questions,\n isBot\n }\n };\n}\n```\n\n## 4. Кэширование и Rate Limiting\n\nРоботам часто требуется больше данных, но медленнее. Используйте разные стратегии кэширования:\n\n```javascript\n// middleware/cacheStrategy.js\nexport function cacheMiddleware(req, res, next) {\n const isBot = req.isBot;\n \n if (isBot) {\n // Для роботов: долгий кэш (24 часа)\n res.setHeader(\n \"Cache-Control\",\n \"public, s-maxage=86400, stale-while-revalidate=604800\"\n );\n } else {\n // Для пользователей: короткий кэш (5 минут)\n res.setHeader(\n \"Cache-Control\",\n \"public, s-maxage=300, stale-while-revalidate=3600\"\n );\n }\n \n next();\n}\n```\n\n## 5. Robots.txt и Sitemap для SEO\n\n```javascript\n// routes/robots.js\nexport function getRobotsText() {\n return `User-agent: *\nAllow: /\nDisallow: /api/\nDisallow: /admin/\nDisallow: /auth/\n\nUser-agent: Googlebot\nAllow: /\n\nUser-agent: Baiduspider\nAllow: /\n\nCrawl-delay: 1\n\nSitemap: https://prepbro.ru/sitemap.xml\n`;\n}\n\n// routes/sitemap.js\nexport async function getSitemap() {\n const questions = await getQuestionsFromDB();\n \n const xml = `\n\n ${questions.map(q => `\n \n https://prepbro.ru/questions/${q.id}\n ${q.updatedAt}\n 0.8\n \n `).join(\"\")}\n`;\n \n return xml;\n}\n```\n\n## 6. Логирование и мониторинг\n\n```javascript\n// middleware/botLogger.js\nexport function logBotActivity(req, res, next) {\n const isBot = req.isBot;\n \n // Логируем боты отдельно для аналитики\n if (isBot) {\n console.log({\n timestamp: new Date().toISOString(),\n type: \"BOT_REQUEST\",\n userAgent: req.userAgent,\n path: req.path,\n method: req.method,\n ip: req.ip\n });\n } else {\n // Для пользователей используем другой уровень логирования\n console.log({\n timestamp: new Date().toISOString(),\n type: \"USER_REQUEST\",\n path: req.path,\n method: req.method,\n ip: req.ip\n });\n }\n \n next();\n}\n```\n\n## Основные моменты\n\n- Определяйте роботов по User-Agent\n- Кэшируйте длительнее для роботов\n- Ограничивайте Rate Limit для роботов мягче\n- Используйте структурированные данные (JSON-LD) для SEO\n- Предоставляйте robots.txt и sitemap.xml\n- Логируйте боты отдельно для аналитики\n- SSR оптимизирует полноту контента для роботов\n\nЭто позволит поисковым системам эффективно индексировать ваш сайт, пока пользователи получат быстрый, оптимизированный ответ.","dateCreated":"2026-04-03T17:55:49.865984","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
PrepBro
← Назад к вопросам

Как разделишь запросы в Node.js для SEO-роботов и пользователей?

2.0 Middle🔥 201 комментариев
#JavaScript Core#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как разделить запросы для SEO-роботов и пользователей

В современных веб-приложениях часто требуется различать запросы от поисковых роботов (Googlebot, Yandexbot) и обычных пользователей. Это необходимо для оптимизации SEO, защиты от скрейпинга и кэширования контента.

1. Определение User-Agent и идентификация роботов

Первый шаг - определить, откуда идет запрос, проверив User-Agent заголовок:

// middleware/detectBot.js
const BOT_USER_AGENTS = [
  "googlebot",
  "bingbot",
  "slurp",
  "duckduckgo",
  "baiduspider",
  "yandexbot",
  "ahrefs",
  "semrush",
  "mj12bot"
];

function isBot(userAgent) {
  if (!userAgent) return false;
  const lowerUA = userAgent.toLowerCase();
  return BOT_USER_AGENTS.some(bot => lowerUA.includes(bot));
}

export function detectBotMiddleware(req, res, next) {
  req.isBot = isBot(req.get("user-agent"));
  req.userAgent = req.get("user-agent");
  next();
}

2. Разные ответы для роботов и пользователей

В Express/FastAPI приложении можно отправлять разные данные в зависимости от типа клиента:

// routes/questions.js
import express from "express";
import { detectBotMiddleware } from "../middleware/detectBot.js";

const router = express.Router();

router.use(detectBotMiddleware);

router.get("/questions", async (req, res) => {
  try {
    const questions = await getQuestionsFromDB();
    
    // Для SEO-роботов отправляем полную версию с метаданными
    if (req.isBot) {
      return res.json({
        type: "bot-response",
        questions: questions.map(q => ({
          id: q.id,
          title: q.title,
          description: q.description,
          category: q.category,
          createdAt: q.createdAt,
          tags: q.tags,
          difficulty: q.difficulty
        })),
        meta: {
          total: questions.length,
          crawlTime: new Date().toISOString()
        }
      });
    }
    
    // Для пользователей - только необходимые данные
    return res.json({
      type: "user-response",
      questions: questions.slice(0, 10).map(q => ({
        id: q.id,
        title: q.title,
        category: q.category
      })),
      hasMore: questions.length > 10
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

export default router;

3. Server-Side Rendering (SSR) для роботов

При SSR, рендерите полный HTML для роботов:

// pages/questions.js (Next.js)
export default function QuestionsPage({ questions, isBot }) {
  return (
    <div>
      <Head>
        {/* Meta теги для SEO */}
        <title>Вопросы на собеседование</title>
        <meta name="description" content="Лучшие вопросы с собеседований" />
        {isBot && (
          <>
            <meta name="robots" content="index, follow" />
            <script type="application/ld+json">
              {JSON.stringify({
                "@context": "https://schema.org",
                "@type": "CollectionPage",
                name: "Вопросы на собеседование",
                itemListElement: questions.map((q, i) => ({
                  "@type": "ListItem",
                  position: i + 1,
                  name: q.title,
                  url: `https://prepbro.ru/questions/${q.id}`
                }))
              })}
            </script>
          </>
        )}
      </Head>
      
      {isBot ? (
        // Полная версия для роботов
        <div className="questions-list-full">
          {questions.map(q => (
            <article key={q.id}>
              <h1>{q.title}</h1>
              <p>{q.description}</p>
              <span>{q.category}</span>
            </article>
          ))}
        </div>
      ) : (
        // Компактная версия для пользователей
        <QuestionsList questions={questions.slice(0, 10)} />
      )}
    </div>
  );
}

export async function getServerSideProps({ req, res }) {
  const isBot = detectBot(req.headers["user-agent"]);
  
  // Увеличиваем кэш для робота запросов
  if (isBot) {
    res.setHeader("Cache-Control", "public, s-maxage=3600, stale-while-revalidate=86400");
  }
  
  const questions = await getQuestionsFromDB({
    limit: isBot ? 100 : 10
  });
  
  return {
    props: {
      questions,
      isBot
    }
  };
}

4. Кэширование и Rate Limiting

Роботам часто требуется больше данных, но медленнее. Используйте разные стратегии кэширования:

// middleware/cacheStrategy.js
export function cacheMiddleware(req, res, next) {
  const isBot = req.isBot;
  
  if (isBot) {
    // Для роботов: долгий кэш (24 часа)
    res.setHeader(
      "Cache-Control",
      "public, s-maxage=86400, stale-while-revalidate=604800"
    );
  } else {
    // Для пользователей: короткий кэш (5 минут)
    res.setHeader(
      "Cache-Control",
      "public, s-maxage=300, stale-while-revalidate=3600"
    );
  }
  
  next();
}

5. Robots.txt и Sitemap для SEO

// routes/robots.js
export function getRobotsText() {
  return `User-agent: *
Allow: /
Disallow: /api/
Disallow: /admin/
Disallow: /auth/

User-agent: Googlebot
Allow: /

User-agent: Baiduspider
Allow: /

Crawl-delay: 1

Sitemap: https://prepbro.ru/sitemap.xml
`;
}

// routes/sitemap.js
export async function getSitemap() {
  const questions = await getQuestionsFromDB();
  
  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  ${questions.map(q => `
    <url>
      <loc>https://prepbro.ru/questions/${q.id}</loc>
      <lastmod>${q.updatedAt}</lastmod>
      <priority>0.8</priority>
    </url>
  `).join("")}
</urlset>`;
  
  return xml;
}

6. Логирование и мониторинг

// middleware/botLogger.js
export function logBotActivity(req, res, next) {
  const isBot = req.isBot;
  
  // Логируем боты отдельно для аналитики
  if (isBot) {
    console.log({
      timestamp: new Date().toISOString(),
      type: "BOT_REQUEST",
      userAgent: req.userAgent,
      path: req.path,
      method: req.method,
      ip: req.ip
    });
  } else {
    // Для пользователей используем другой уровень логирования
    console.log({
      timestamp: new Date().toISOString(),
      type: "USER_REQUEST",
      path: req.path,
      method: req.method,
      ip: req.ip
    });
  }
  
  next();
}

Основные моменты

  • Определяйте роботов по User-Agent
  • Кэшируйте длительнее для роботов
  • Ограничивайте Rate Limit для роботов мягче
  • Используйте структурированные данные (JSON-LD) для SEO
  • Предоставляйте robots.txt и sitemap.xml
  • Логируйте боты отдельно для аналитики
  • SSR оптимизирует полноту контента для роботов

Это позволит поисковым системам эффективно индексировать ваш сайт, пока пользователи получат быстрый, оптимизированный ответ.