<!-- Generated from ru/llms-txt-cms/index.html. The canonical document is the HTML page. -->

- [ Главная ](/ru/) 
/
- [ Как создать ](/ru/how-to-create/) 
/
- Руководство по headless CMS            
# `llms.txt` для headless CMS

Headless CMS-платформы (Contentful, Sanity, Strapi, Directus) не обслуживают файлы напрямую: создавайте llms.txt во время сборки из данных CMS или получайте его во время запроса через серверный маршрут.

Последнее обновление: 22 апреля 2026 г.

На этой странице

- [ Основная схема: API CMS → шаг сборки → статический файл ](#pattern)
- [ Пример Contentful ](#contentful)
- [ Пример Sanity ](#sanity)
- [ Пример Strapi ](#strapi)
- [ Интеграция с обработчиком маршрутов Next.js ](#nextjs)
- [ Интеграция с endpoint Astro ](#astro)
- [ Статическая и динамическая генерация ](#static-vs-dynamic)
- [ Контрольный список ](#checklist)            
Используете конструктор сайтов вместо headless CMS? См. специальные руководства для
[Shopify](/ru/llms-txt-shopify/), [Webflow](/ru/llms-txt-webflow/),
[Wix](/ru/llms-txt-wix/) и [Squarespace](/ru/llms-txt-squarespace/).

## Основной шаблон: API CMS → шаг сборки → статический файл

Headless CMS управляют контентом, но не отдают произвольные файлы по произвольным путям. Чтобы
опубликовать `llms.txt`, нужно получать содержимое из вашей CMS и самостоятельно
записывать файл либо в **во время сборки** (статический результат) или в **время запроса** (серверный маршрут).

- **Запросите данные из CMS**, получайте опубликованные страницы с полями title,
slug и summary. 
- **Преобразуйте в ссылки Markdown**, оформите каждую запись как `- [Title](https://url/): description.` 
- **Записать или вернуть файл**, запишите в `public/llms.txt` во время сборки
или вернуть из серверного маршрута во время запроса.     
Примечание

Статическое и динамическое содержимое

Статическая генерация проще и быстрее, файл кэшируется CDN. Динамическая генерация (серверный
маршрут) гарантирует актуальность файла без пересборки. Для большинства сайтов на CMS правильным
выбором будет статическая генерация с ежедневной пересборкой или пересборкой при каждом
развёртывании.

## Пример Contentful

Используйте [JavaScript SDK Contentful](https://github.com/contentful/contentful.js) 
для получения записей через Content Delivery API. Запускайте этот скрипт на этапе сборки (например,
в `package.json` скриптах или вашем CI-конвейере) до сборки фреймворка:
scripts/generate-llms-txt.mjs (Contentful)    Копировать     

```
// scripts/generate-llms-txt.mjs
// Contentful: fetch published doc entries and generate llms.txt at build time

import { createClient } from 'contentful';
import { writeFileSync } from 'fs';

const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});

async function generateLlmsTxt() {
// Fetch entries of content type 'docPage' sorted by display order
const entries = await client.getEntries({
content_type: 'docPage',
order: 'fields.order',
select: 'fields.title,fields.slug,fields.summary',
limit: 100,
});

const SITE_URL = process.env.SITE_URL || 'https://yoursite.com';

const links = entries.items
.map((entry) => {
const { title, slug, summary } = entry.fields;
return `- [${title}](${SITE_URL}/docs/${slug}/): ${summary ?? ''}`;
})
.join('\n');

const content = [
'# Your Site',
'',
'> One-sentence description of your product.',
'',
'## Documentation',
'',
links,
'',
'## Optional',
'',
`- [Changelog](${SITE_URL}/changelog/): Release history.`,
].join('\n');

writeFileSync('public/llms.txt', content, 'utf-8');
console.log(`Generated llms.txt with ${entries.items.length} entries.`);
}

generateLlmsTxt().catch(console.error);

```

Добавьте скрипт в конвейер сборки:
package.json    Копировать     

```
{
"scripts": {
"prebuild": "node scripts/generate-llms-txt.mjs",
"build": "next build"
}
}
```

## Пример Sanity

Используйте GROQ, язык запросов Sanity, чтобы получать только необходимые поля. `!(_id in path("drafts.**"))` фильтр гарантирует включение только опубликованных документов:
scripts/generate-llms-txt.mjs (Sanity)    Копировать     

```
// scripts/generate-llms-txt.mjs
// Sanity: use GROQ to query published docs and generate llms.txt

import { createClient } from '@sanity/client';
import { writeFileSync } from 'fs';

const client = createClient({
projectId: process.env.SANITY_PROJECT_ID,
dataset: process.env.SANITY_DATASET || 'production',
useCdn: false, // always fetch fresh data at build time
apiVersion: '2024-01-01',
});

async function generateLlmsTxt() {
// GROQ query: fetch all published doc entries with title, slug, and summary
const docs = await client.fetch(
`*[_type == "doc" && !(_id in path("drafts.**"))] | order(order asc) {
title,
"slug": slug.current,
summary
}`
);

const SITE_URL = process.env.SITE_URL || 'https://yoursite.com';

const links = docs
.map((doc) => `- [${doc.title}](${SITE_URL}/docs/${doc.slug}/): ${doc.summary ?? ''}`)
.join('\n');

const content = [
'# Your Site',
'',
'> One-sentence description of your product.',
'',
'## Documentation',
'',
links,
].join('\n');

writeFileSync('public/llms.txt', content, 'utf-8');
console.log(`Generated llms.txt with ${docs.length} docs.`);
}

generateLlmsTxt().catch(console.error);

```

## Пример Strapi

Strapi предоставляет REST API по адресу `/api/:collection`. Используйте `fields` и `filters` параметры запроса, чтобы получать только опубликованные
документы с нужными вам полями:
scripts/generate-llms-txt.mjs (Strapi)    Копировать     

```
// scripts/generate-llms-txt.mjs
// Strapi v4/v5: fetch published articles via REST API and generate llms.txt

import { writeFileSync } from 'fs';

const STRAPI_URL = process.env.STRAPI_URL || 'http://localhost:1337';
const STRAPI_TOKEN = process.env.STRAPI_API_TOKEN;
const SITE_URL = process.env.SITE_URL || 'https://yoursite.com';

async function generateLlmsTxt() {
// Fetch published docs, adjust the collection slug and fields as needed
const res = await fetch(
`${STRAPI_URL}/api/docs?fields[0]=title&fields[1]=slug&fields[2]=summary&filters[publishedAt][$notNull]=true&pagination[limit]=100`,
{
headers: STRAPI_TOKEN ? { Authorization: `Bearer ${STRAPI_TOKEN}` } : {},
}
);

if (!res.ok) throw new Error(`Strapi API error: ${res.status}`);
const { data } = await res.json();

const links = data
.map((item) => {
const { title, slug, summary } = item.attributes ?? item; // v4 vs v5
return `- [${title}](${SITE_URL}/docs/${slug}/): ${summary ?? ''}`;
})
.join('\n');

const content = [
'# Your Site',
'',
'> One-sentence description of your product.',
'',
'## Documentation',
'',
links,
].join('\n');

writeFileSync('public/llms.txt', content, 'utf-8');
console.log(`Generated llms.txt with ${data.length} entries.`);
}

generateLlmsTxt().catch(console.error);

```

Strapi v4 оборачивает данные ответа в `attributes` объект; Strapi v5 возвращает поля на
верхнем уровне. Пример обрабатывает оба варианта с запасным значением.

## Интеграция с обработчиком маршрута Next.js

Если вы хотите постоянно синхронизировать файл без полной пересборки, используйте обработчик
маршрута Next.js App Router с `revalidate` с нужным TTL. Next.js кэширует ответ и повторно
создаёт его в фоновом режиме:
app/llms.txt/route.ts (Next.js + Contentful)    Копировать     

```
// app/llms.txt/route.ts
// Next.js App Router, fetch from CMS at request time (or cache with revalidate)

import { NextResponse } from 'next/server';
import { createClient } from 'contentful';

// Cache for 1 hour (Next.js incremental static regeneration)
export const revalidate = 3600;

const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
});

export async function GET() {
const entries = await client.getEntries({
content_type: 'docPage',
order: 'fields.order',
select: 'fields.title,fields.slug,fields.summary',
limit: 100,
});

const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://yoursite.com';

const links = entries.items
.map((e: any) => `- [${e.fields.title}](${SITE_URL}/docs/${e.fields.slug}/): ${e.fields.summary ?? ''}`)
.join('\n');

const body = [
'# Your Site',
'',
'> One-sentence description.',
'',
'## Documentation',
'',
links,
].join('\n');

return new NextResponse(body, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
},
});
}

```

## Интеграция с конечной точкой Astro

В Astro создайте `src/pages/llms.txt.ts` конечной точкой с `export const prerender = true` для генерации файла во время сборки. Astro вызовет API
CMS во время `astro build` и записать статический файл в `dist/llms.txt`:
src/pages/llms.txt.ts (Astro + Sanity)    Копировать     

```
// src/pages/llms.txt.ts
// Astro endpoint, fetch from CMS at build time (static generation)

import type { APIRoute } from 'astro';
import { createClient } from '@sanity/client';

// This endpoint is pre-rendered at build time
export const prerender = true;

const sanity = createClient({
projectId: import.meta.env.SANITY_PROJECT_ID,
dataset: import.meta.env.SANITY_DATASET || 'production',
useCdn: false,
apiVersion: '2024-01-01',
});

export const GET: APIRoute = async () => {
const docs = await sanity.fetch(
`*[_type == "doc" && !(_id in path("drafts.**"))] | order(order asc) {
title, "slug": slug.current, summary
}`
);

const SITE_URL = import.meta.env.SITE_URL || 'https://yoursite.com';

const links = docs
.map((doc: any) => `- [${doc.title}](${SITE_URL}/docs/${doc.slug}/): ${doc.summary ?? ''}`)
.join('\n');

const body = [
'# Your Site',
'',
'> One-sentence description of your product.',
'',
'## Documentation',
'',
links,
].join('\n');

return new Response(body, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
};

```

## Статическая и динамическая генерация

- **Статически (во время сборки)**, быстрее (кэшируется CDN), проще, без
зависимости от CMS во время выполнения. Лучше всего подходит, когда содержимое меняется редко
или вы развёртываете проект при каждом изменении. 
- **Динамический (маршрут сервера)**, всегда отражает последнее содержимое CMS.
Лучше всего подходит, когда контент часто меняется между развёртываниями или когда вы не
можете запустить пересборку при изменении контента. Добавьте `Cache-Control` заголовок,
чтобы не перегружать API CMS каждым запросом.   
## Контрольный список

- Скрипт или конечная точка загружает только **опубликовано** содержимое (не черновики). 
- Все сгенерированные URL **абсолютный** (`https://`). 
- Файл начинается ровно с одного H1. 
- Сразу после H1 следует сводка в blockquote. 
- В каждом разделе есть хотя бы одна ссылка. 
- Файл меньше 20 КБ (отбирайте содержимое, не выгружайте каждую запись). 
- Шаг сборки выполняется перед сборкой фреймворка (`prebuild` скрипт или шаг CI). 
- Проверено с помощью [llmtxt.info/validator/](/ru/validator/) после каждого развёртывания.   
## Связанные руководства

- [Как создать llms.txt](/ru/how-to-create/), шаблоны и руководство по развёртыванию. 
- [Руководство по Next.js](/ru/llms-txt-nextjs/), App Router и статическая генерация. 
- [Руководство по Astro](/ru/llms-txt-astro/), коллекции контента и конечные точки. 
- [Справочник формата llms.txt](/ru/llms-txt-format/), подробности спецификации. 
- [Валидатор](/ru/validator/) · [Генератор](/ru/generator/).        
## Источники

- [ llmstxt.org, предложение сообщества ](https://llmstxt.org/)
- [ JavaScript SDK Contentful ](https://github.com/contentful/contentful.js)
- [ Справочник Sanity GROQ ](https://www.sanity.io/docs/content-lake/how-queries-work)
- [ Документация Strapi REST API ](https://docs.strapi.io/cms/api/rest)
