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

- [ 首页 ](/zh/) 
/
- [ 如何创建 ](/zh/how-to-create/) 
/
- 无头 CMS 指南            
# 面向无头 CMS 的 `llms.txt`

无头 CMS 平台（Contentful、Sanity、Strapi、Directus）不直接提供文件；你可以在构建时根据 CMS 数据生成 llms.txt，或通过服务器路由在请求时获取。

最近更新: 2026年4月22日

本页内容

- [ 核心模式：CMS API → 构建步骤 → 静态文件 ](#pattern)
- [ Contentful 示例 ](#contentful)
- [ Sanity 示例 ](#sanity)
- [ Strapi 示例 ](#strapi)
- [ 与 Next.js 路由处理程序集成 ](#nextjs)
- [ 与 Astro 端点集成 ](#astro)
- [ 静态生成与动态生成 ](#static-vs-dynamic)
- [ 检查清单 ](#checklist)            
使用托管式网站构建器而不是无头 CMS？请参阅以下平台的专门指南：
[Shopify](/zh/llms-txt-shopify/), [Webflow](/zh/llms-txt-webflow/),
[Wix](/zh/llms-txt-wix/) 和 [Squarespace](/zh/llms-txt-squarespace/).

## 核心模式：CMS API → 构建步骤 → 静态文件

Headless CMS 平台负责管理内容，但不能在任意路径提供任意文件。要发布 `llms.txt`，你需要从 CMS 拉取内容并自行写入文件，方式可以是 **构建时** （静态输出）或在 **请求时** （服务端路由）。

- **查询 CMS**，获取带有标题、slug 和摘要字段的已发布页面。 
- **映射为 Markdown 链接**，将每个条目格式化为 `- [Title](https://url/): description.` 
- **写入或返回文件**，写入 `public/llms.txt` 在构建时写入文件，或在请求时通过服务器路由返回。     
备注

静态与动态

静态生成更简单、更快，文件会被你的 CDN
缓存。动态生成（服务器路由）可确保文件始终最新，无需重新构建。对于大多数基于 CMS
的站点，静态生成并按天或按部署重建是最佳选择。

## Contentful 示例

使用 [Contentful JavaScript SDK](https://github.com/contentful/contentful.js) 
通过 Content Delivery API 获取条目。在构建步骤中运行此脚本（例如在 `package.json` scripts
或你的 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 示例

使用 Sanity 的查询语言 GROQ 精确获取所需字段。该 `!(_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 在 `/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 路由处理程序集成

如果您希望文件始终保持同步且无需完整重建，请使用带有 `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 会在 构建期间调用 CMS API。
`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` 响应头，以免每次请求都对 CMS API 造成过大压力。   
## 检查清单

- 脚本或端点仅获取 **已发布** 内容（而非草稿）。 
- 所有生成的 URL 均为 **绝对** (`https://`). 
- 文件开头恰好只有一个 H1。 
- blockquote 摘要紧跟在 H1 之后。 
- 每个章节至少包含一个链接。 
- 文件小于 20 KB（筛选内容，不要倾倒所有条目）。 
- 构建步骤在框架构建之前运行（`prebuild` 脚本或 CI 步骤）。 
- 已使用 [llmtxt.info/validator/](/zh/validator/) 在每次部署后。   
## 相关指南

- [如何创建 llms.txt](/zh/how-to-create/)、模板和部署指南。 
- [Next.js 指南](/zh/llms-txt-nextjs/)，App Router 和静态生成。 
- [Astro 指南](/zh/llms-txt-astro/)、内容集合和端点。 
- [llms.txt 格式参考](/zh/llms-txt-format/)，规范细节。 
- [验证器](/zh/validator/) · [生成器](/zh/generator/).        
## 来源

- [ llmstxt.org，社区提案 ](https://llmstxt.org/)
- [ Contentful JavaScript SDK ](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)
