Headless CMS OSS比較:Strapi vs Directus vs Payload CMS でContentful代替をセルフホスト
オープンソースラボ編集部 ・ 2026年6月13日
Headless CMS OSS比較:Strapi vs Directus vs Payload CMS でContentful代替をセルフホスト
ContentfulはFreeプランでAPIリクエスト5万回/月・Proプランは月$300〜とエンタープライズに偏ったコスト構造です。Strapi(最も採用実績多・Node.js)・Directus(DB-First・SQLを直接管理)・Payload CMS(TypeScript-First・Next.jsと深く統合)はOSSのHeadless CMSで、Next.js/Nuxt.js/Astro等のフロントエンドにコンテンツAPIを提供できます。
Headless CMSの選定理由
- コンテンツ管理のUI分離: エンジニアでないライターがコンテンツを管理できる管理画面を自前提供
- REST/GraphQL API: フロントエンドフレームワークに依存しないAPIでコンテンツを配信
- 型安全なコンテンツモデル: ブログ記事・商品・FAQ等のコンテンツ構造をスキーマで定義
- メディア管理: 画像・動画をWebP変換・サイズ最適化付きで管理
- 多言語対応: i18n対応でJA/EN等の複数言語コンテンツを管理
主要ツールの概要
Strapi
2015年からNode.js(TypeScript)で開発されているHeadless CMSです。GitHubスター63k+。プラグインエコシステム・カスタムAPI・ロールベースアクセス制御(RBAC)・GraphQL・i18nが標準搭載で、Headless CMSカテゴリで最も多くの商用採用実績を持ちます。
# Strapiプロジェクトを作成(SQLite・PostgreSQL対応)
npx create-strapi-app@latest my-cms --dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi --dbusername=strapi --dbpassword=strapi_pass --no-run
cd my-cms && npm run develop
# http://localhost:1337/admin でUI起動
// Strapi コンテンツタイプ定義(TypeScript)
// src/api/article/content-types/article/schema.json
{
"kind": "collectionType",
"collectionName": "articles",
"info": {
"singularName": "article",
"pluralName": "articles",
"displayName": "Article"
},
"options": {
"draftAndPublish": true
},
"pluginOptions": {
"i18n": { "localized": true }
},
"attributes": {
"title": {
"pluginOptions": { "i18n": { "localized": true } },
"type": "string",
"required": true
},
"content": {
"pluginOptions": { "i18n": { "localized": true } },
"type": "richtext"
},
"slug": {
"type": "uid",
"targetField": "title"
},
"cover": {
"type": "media",
"multiple": false,
"required": false,
"allowedTypes": ["images"]
},
"category": {
"type": "relation",
"relation": "manyToOne",
"target": "api::category.category"
},
"tags": {
"type": "json"
},
"seo": {
"type": "component",
"repeatable": false,
"component": "shared.seo"
}
}
}
// Next.js App Router + Strapi でコンテンツを取得
// lib/strapi.ts
const STRAPI_URL = process.env.STRAPI_URL!;
const STRAPI_TOKEN = process.env.STRAPI_API_TOKEN!;
async function fetchStrapi<T>(path: string, params?: Record<string, string>): Promise<T> {
const qs = params ? '?' + new URLSearchParams(params).toString() : '';
const res = await fetch(`${STRAPI_URL}/api${path}${qs}`, {
headers: { Authorization: `Bearer ${STRAPI_TOKEN}` },
next: { revalidate: 60 }, // ISR: 60秒ごとに再検証
});
const json = await res.json();
return json.data;
}
// 記事一覧を取得
export async function getArticles(locale = 'ja') {
return fetchStrapi<Article[]>('/articles', {
'populate': 'cover,category,tags',
'filters[publishedAt][$notNull]': 'true',
'locale': locale,
'sort': 'publishedAt:desc',
'pagination[limit]': '20',
});
}
// スラッグで記事を取得
export async function getArticleBySlug(slug: string, locale = 'ja') {
const data = await fetchStrapi<Article[]>('/articles', {
'filters[slug][$eq]': slug,
'populate': 'cover,category,tags,seo',
'locale': locale,
});
return data[0] || null;
}
// app/articles/[slug]/page.tsx
export default async function ArticlePage({ params }: { params: { slug: string } }) {
const article = await getArticleBySlug(params.slug);
if (!article) notFound();
return (
<article>
<h1>{article.attributes.title}</h1>
<div dangerouslySetInnerHTML={{ __html: article.attributes.content }} />
</article>
);
}
Directus
2004年から開発されているDB-FirstのHeadless CMSです。GitHubスター27k+。既存のPostgreSQL/MySQL/SQLiteのテーブルをそのままCMS化できるのが特徴で、エンジニアが設計したデータベーススキーマをライターが使えるCMS管理画面に変換します。Strapi等とは逆のアプローチで「DBが主役・CMSが管理画面」という思想です。
# Directusをdocker-composeで起動
version: "3"
services:
directus:
image: directus/directus:10.11
restart: unless-stopped
ports:
- "8055:8055"
volumes:
- directus_uploads:/directus/uploads
- directus_extensions:/directus/extensions
depends_on:
database:
condition: service_healthy
cache:
condition: service_started
environment:
SECRET: "your-directus-secret-key"
DB_CLIENT: "pg"
DB_HOST: "database"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus_pass"
CACHE_ENABLED: "true"
CACHE_STORE: "redis"
CACHE_REDIS: "redis://cache:6379"
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "your-admin-password"
PUBLIC_URL: "https://cms.yoursite.com"
CORS_ENABLED: "true"
CORS_ORIGIN: "https://yoursite.com"
STORAGE_LOCATIONS: "local"
STORAGE_LOCAL_DRIVER: "local"
STORAGE_LOCAL_ROOT: "/directus/uploads"
database:
image: postgres:16-alpine
restart: unless-stopped
healthcheck:
test: ['CMD', 'pg_isready', '-d', 'directus', '-U', 'directus']
interval: 5s
timeout: 5s
retries: 5
environment:
POSTGRES_USER: directus
POSTGRES_PASSWORD: directus_pass
POSTGRES_DB: directus
volumes:
- directus_db:/var/lib/postgresql/data
cache:
image: redis:7-alpine
restart: unless-stopped
volumes:
directus_db:
directus_uploads:
directus_extensions:
// Next.js + Directus SDK でコンテンツを取得
// npm install @directus/sdk
import { createDirectus, rest, readItems, readItem, authentication } from '@directus/sdk';
const directus = createDirectus(process.env.DIRECTUS_URL!)
.with(authentication('static', { token: process.env.DIRECTUS_TOKEN! }))
.with(rest());
// 記事一覧取得
export async function getArticles() {
return directus.request(readItems('articles', {
fields: ['id', 'title', 'slug', 'summary', 'published_date', 'category.*', 'cover.*'],
filter: { status: { _eq: 'published' } },
sort: ['-published_date'],
limit: 20,
}));
}
// ウェブフックで変更時にNext.js ISRを再検証
// Directus Settings → Webhooks → POST https://yoursite.com/api/revalidate
// api/revalidate/route.ts
export async function POST(request: Request) {
const secret = request.headers.get('x-webhook-secret');
if (secret !== process.env.REVALIDATION_SECRET) {
return new Response('Unauthorized', { status: 401 });
}
revalidatePath('/articles');
revalidatePath('/');
return Response.json({ revalidated: true });
}
Payload CMS
2021年からTypeScriptで開発されているコードファーストのHeadless CMSです。GitHubスター27k+。コレクション・フィールド・バリデーション・アクセス制御すべてをTypeScriptで定義し、型安全なSDKでNext.jsのサーバーコンポーネントから直接DBにアクセスできます。Next.js App Routerとの統合が最も深く、同じプロジェクト内にPayload CMSとNext.jsを共存させる「Payload + Next.js Monorepo」が特徴的です。
// payload.config.ts(Payload CMSの設定)
import { buildConfig } from 'payload/config';
import { postgresAdapter } from '@payloadcms/db-postgres';
import { lexicalEditor } from '@payloadcms/richtext-lexical';
import { s3Storage } from '@payloadcms/storage-s3';
export default buildConfig({
serverURL: process.env.NEXT_PUBLIC_SERVER_URL!,
admin: {
user: 'users',
bundler: viteBundler(),
},
db: postgresAdapter({
pool: { connectionString: process.env.DATABASE_URL! },
}),
editor: lexicalEditor({}),
plugins: [
s3Storage({
collections: { media: true },
bucket: process.env.S3_BUCKET!,
config: {
credentials: { accessKeyId: process.env.S3_ACCESS_KEY!, secretAccessKey: process.env.S3_SECRET! },
region: process.env.S3_REGION!,
},
}),
],
collections: [
{
slug: 'articles',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'category', 'status', 'publishedAt'],
},
access: {
read: () => true, // 公開記事は誰でも読める
create: isAdmin,
update: isAdminOrAuthor,
delete: isAdmin,
},
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'slug', type: 'text', unique: true, required: true, admin: { position: 'sidebar' } },
{ name: 'content', type: 'richText' },
{ name: 'category', type: 'relationship', relationTo: 'categories', required: true },
{ name: 'cover', type: 'upload', relationTo: 'media' },
{ name: 'status', type: 'select', options: ['draft', 'published'], defaultValue: 'draft', admin: { position: 'sidebar' } },
{ name: 'publishedAt', type: 'date', admin: { position: 'sidebar' } },
],
hooks: {
afterChange: [revalidateArticle], // 変更時にNext.js ISRを再検証
},
},
],
});
機能比較表
| 比較項目 | Strapi | Directus | Payload CMS |
|---|---|---|---|
| ライセンス | SSPL(v5) | BUSL | MIT |
| 言語 | JavaScript/TS | JavaScript/TS | TypeScript |
| DB-First | ❌ | ✅ | ❌ |
| 型安全SDK | 部分的 | ✅ | ✅ 完全 |
| Next.js統合 | ✅ | ✅ | ✅ 最深 |
| GraphQL | ✅ | ✅ | ✅ |
| i18n | ✅ 標準 | ✅ | プラグイン |
| GitHub Stars | 63k+ | 27k+ | 27k+ |
Headless CMSとCDNの組み合わせはDevOpsカテゴリ/categories/devopsを参照。コンテンツ管理と組み合わせるナレッジベースツールは/categories/knowledgeにまとめています。
FAQ
Q. StrapiとPayload CMSの最大の違いは何ですか?
A. 設計思想が根本的に異なります。Strapi: 「ノーコードでコンテンツモデルを定義」 - 管理UIからコレクションを作成し、生成されたAPIを使う。エンジニアでなくても管理できる。プロジェクトが成長するにつれてカスタマイズが難しくなることがある。Payload CMS: 「コードでコンテンツモデルを定義(Configuration as Code)」 - TypeScriptで型安全にコレクション・フィールド・バリデーション・アクセス制御を定義する。DBスキーマはPayloadが自動生成。Gitで管理でき、チームで変更をレビューできる。選択基準: CMSの設定をコードで管理したい・Next.js App Routerと最深レベルで統合したい→Payload。ライター/非エンジニアが管理UI上でコンテンツモデルを変更できるようにしたい・プラグインエコシステムが重要→Strapi。
Q. DirectusでSupabase(PostgreSQL)のテーブルを直接Headless CMS化できますか?
A. Directusは既存のPostgreSQLデータベースに接続してそのテーブルを管理UIで操作できます。Supabaseとの接続方法: SupabaseのSettings → Database → Connection InfoでPostgres接続文字列を取得し、DirectusのDB設定に使います。注意点: ①Directusはインストール時にシステムテーブル(directus_users・directus_collections等)をDBに追加します②SupabaseのRowLevelSecurity(RLS)はDirectusのサーバー側接続(supabaseサービスロール)では無効化されます③Supabaseのネイティブ認証(Auth)とDirectusのAuth(独自)は独立しているためユーザー管理が二重になります。推奨構成: SupabaseをDBとしてのみ使い、認証・管理UIはDirectusに統一するか、もしくはSupabaseのAuth+Directusを別DBで動かすハイブリッドです。
Q. Payload CMSをNext.js App Routerと同じプロジェクト内に共存させる方法は?
A. Payload CMS v3はNext.js App RouterとのMonorepo共存をサポートしています。セットアップ:
# Payload Next.jsテンプレートで新規プロジェクト作成
npx create-payload-app@latest my-site --template website
cd my-site && pnpm install && pnpm run dev
# CMSと Next.js が http://localhost:3000 で同時起動
# /admin でPayload CMS管理画面
# / でNext.jsフロントエンド
この構成の利点: ①DBアクセスが直接(APIリクエスト不要)でサーバーコンポーネントからpayload.find({collection: 'articles'})を直接呼び出せる②Next.jsとPayloadが同じTypeScript型を共有③デプロイがシングルVercel/Railway デプロイで完結。制限: VercelのEdge Runtimeでは動作しないためruntime = 'nodejs'が必要です。
Q. ContentfulからStrapiへの移行手順を教えてください。
A. Contentfulからのコンテンツ移行は「スキーマ移行」と「コンテンツデータ移行」の2ステップです。①スキーマ移行: ContentfulのContent Model(JSON)をエクスポート→Strapi管理UIでコレクションとして手動再作成(Content TypeのフィールドはほぼStrapiで対応するフィールドタイプが存在)②コンテンツ移行: Contentful Management APIで全エントリをJSONエクスポート→変換スクリプト(contentful-field名→strapi-field名マッピング)→Strapi REST APIでバルクインポート。メディアファイル: ContentfulのMediaをダウンロードしてStrapiのMedia Libraryにアップロード(スクリプトで自動化)。フロントエンドの変更: contentful SDKを @strapi/sdk に差し替えてAPIエンドポイントを変更。Contentfulのrich textはStrapiのMarkdownまたはRichtextに変換。
まとめ
| ユースケース | 推奨ツール |
|---|---|
| 最多採用実績・プラグイン豊富 | Strapi |
| 既存DBをCMS化・DB-First | Directus |
| Next.js深統合・TypeScript型安全 | Payload CMS |
| Contentful代替を今すぐ始めたい | Strapi |