開發者正在放棄 CMS 的原因,以及 2026 年更好的替代方案

我已經用 headless CMS 平台建立網站多年了。Contentful、Sanity、Strapi -- 你能說出來的,我都整合過。但在 2025 年末左右,我開始注意到一個模式:對於越來越多的專案,我根本沒有使用 CMS。相反,我用 Next.js 在 Vercel 上、Supabase 作為資料和認證、Claude API 處理任何內容智能需求來運送完整的全棧應用。沒有 CMS 廠商。沒有內容建模 UI。沒有按月計費的座位許可。

這不是為了標新立異。確實有很多專案適合用 headless CMS -- 我們在 Social Animal 建立了很多這樣的專案(看看我們的headless CMS 開發工作)。但有一類特定的應用,這種無廠商棧不僅可行,而是更好。讓我詳細為你介紹它的工作原理、何時使用它,以及如何從零開始設定。

目錄

為什麼開發者正在放棄 CMS

讓我們誠實地談談 CMS 提供的東西:一個非技術人員編輯內容的 UI、一個結構化的資料層、也許還有一些媒體管理,以及一個 API 來取得所有這些。當你有一個行銷團隊每天發佈文章時,這確實很有價值。

但這是我在 2026 年一直看到的:

  • SaaS 產品,其中「內容」是用戶生成的資料,而不是編輯副本
  • 內部工具,其中團隊足夠技術化,可以直接編輯資料庫或使用輕量級管理面板
  • AI 原生應用,其中內容即時生成、摘要或轉換
  • 初創公司無法為 CMS 辯護 $300-500/月 的成本,當他們只有三個用戶時

對於這些專案,CMS 是開銷。你為不會使用的內容建模功能付費,管理一項服務的 API 密鑰,該服務基本上只是一個花哨的資料庫包裝器,並且需要處理 webhook 複雜性來保持同步。

替代方案?完全擁有你的資料層。Supabase 為你提供 Postgres(一個真正的資料庫,不是專有的內容存儲)、認證、檔案儲存和即時訂閱。Claude 處理智能層。Next.js 和 Vercel 處理其他一切。

堆疊概覽

層級 技術 角色 2026 年定價(起價)
前端和 API Next.js 15 (App Router) UI、Server Components、Route Handlers 免費(開源)
託管和邊緣 Vercel 部署、CDN、無伺服器函數 免費層 / $20/月 Pro
資料庫和認證 Supabase Postgres、行級安全性、認證、儲存 免費層 / $25/月 Pro
AI 層 Claude API (Anthropic) 內容生成、摘要、分類 按令牌計費(約 $3/$15 每 100 萬令牌用於 Sonnet 4)
管理 UI 自訂(React + Supabase) 你的團隊的內容管理 $0(你自己構建)

中等流量的生產應用的總成本:$45-100/月。與典型的 headless CMS 設定相比,僅 CMS 本身就可能花費 $99-500/月,更不用說你還要為託管付費。

在 Vercel 上設定 Next.js 專案

我假設你已經有 Node.js 20+ 和一個 Vercel 帳戶。如果你是 Next.js 的新手,我們的團隊在我們的Next.js 開發能力頁面上已經廣泛撰寫過它。

npx create-next-app@latest my-app --typescript --tailwind --app --src-dir
cd my-app

Next.js 15 配合 App Router 是這裡的基礎。我們預設使用 Server Components,這意味著大多數數據獲取發生在伺服器上 -- 沒有暴露的 API 密鑰,沒有初始內容的客戶端載入旋轉器。

這是我為這個堆疊的典型專案結構:

src/
├── app/
│   ├── (public)/           # 行銷頁面、文章
│   ├── (dashboard)/        # 經過認證的管理區域
│   │   ├── layout.tsx      # 認證檢查包裝器
│   │   ├── posts/
│   │   ├── media/
│   │   └── settings/
│   ├── api/
│   │   ├── ai/             # Claude API 路由
│   │   └── webhooks/       # Supabase 即時鉤子
│   └── layout.tsx
├── lib/
│   ├── supabase/
│   │   ├── client.ts       # 瀏覽器客戶端
│   │   ├── server.ts       # 伺服器客戶端
│   │   └── admin.ts        # 服務角色客戶端
│   ├── claude.ts           # Anthropic SDK 包裝器
│   └── utils.ts
├── components/
└── types/

環境變數

你在 .env.local 中需要這些:

NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-key
ANTHROPIC_API_KEY=sk-ant-...

部署到 Vercel

推送到 GitHub。在 Vercel 中連接該 repo。添加你的環境變數。完成。我不會詳述這一點 -- Vercel 對 Next.js 部署的開發者體驗是業界最好的,你可能已經知道它是如何工作的。

值得注意的一件事:如果你需要功能標誌或配置在不重新部署的情況下更新,請使用 Vercel 的 Edge Config。這是一個小事,但它替代了另一個 SaaS 工具。

Supabase 作為後端:認證、資料庫和儲存

這是魔力發生的地方。Supabase 不只是「Firebase 但用 Postgres」-- 它是一個完整的後端平台,你實際上擁有它。你的資料存放在標準 PostgreSQL 資料庫中。如果你曾經想要離開,你 pg_dump 然後離開。試試用專有的 CMS 做這個。

資料庫模式

假設你正在構建一個內容驅動的應用(你通常會為了 CMS 而使用的東西)。這裡有一個模式,可以處理文章、媒體和基本分類法:

-- 啟用 UUID 生成
create extension if not exists "uuid-ossp";

-- 內容表(替代你的 CMS 內容模型)
create table public.posts (
  id uuid default uuid_generate_v4() primary key,
  title text not null,
  slug text unique not null,
  body text, -- Markdown 內容
  excerpt text,
  status text default 'draft' check (status in ('draft', 'published', 'archived')),
  author_id uuid references auth.users(id),
  featured_image text, -- Supabase Storage 路徑
  metadata jsonb default '{}', -- 靈活的欄位,無需遷移
  published_at timestamptz,
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);

-- 標籤 / 分類法
create table public.tags (
  id uuid default uuid_generate_v4() primary key,
  name text unique not null,
  slug text unique not null
);

create table public.post_tags (
  post_id uuid references public.posts(id) on delete cascade,
  tag_id uuid references public.tags(id) on delete cascade,
  primary key (post_id, tag_id)
);

-- 行級安全性
alter table public.posts enable row level security;

-- 任何人都可以讀取已發布的文章
create policy "Public can read published posts"
  on public.posts for select
  using (status = 'published');

-- 經過認證的用戶可以管理自己的文章
create policy "Authors can manage own posts"
  on public.posts for all
  using (auth.uid() = author_id);

那個 metadata jsonb 欄位是關鍵。它為你提供了 CMS 自訂欄位的靈活性,而無需每次行銷團隊想要新欄位時運行遷移。需要 SEO 描述?metadata->>'seo_description'。需要 Open Graph 圖像覆蓋?metadata->>'og_image'。在你需要靈活性的地方它是無模式的,在你需要完整性的地方是結構化的。

認證設定

Supabase Auth 處理一切。電子郵件/密碼、魔法連結、使用 Google/GitHub 的 OAuth -- 這一切都已內置。

// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export async function createClient() {
  const cookieStore = await cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) =>
            cookieStore.set(name, value, options)
          )
        },
      },
    }
  )
}

檔案儲存

Supabase Storage 替代了你的 CMS 擁有的任何媒體庫。建立一個名為 media 的桶,設定一個策略,你就有了一個帶有自動 CDN URL 的 S3 相容檔案存儲。

// 上傳一個檔案
const { data, error } = await supabase.storage
  .from('media')
  .upload(`posts/${slug}/${file.name}`, file, {
    cacheControl: '3600',
    upsert: false,
  })

// 獲取公共 URL
const { data: { publicUrl } } = supabase.storage
  .from('media')
  .getPublicUrl(`posts/${slug}/${file.name}`)

整合 Claude API 進行內容智能

這是 2026 年堆疊最與傳統 Web 開發不同的地方。Claude API 不只是一個聊天機器人 -- 它是一個智能層,可以替代整個 CMS 插件和第三方服務類別。

這是我在生產中使用它的用途:

  • 自動生成 SEO 元數據來自文章內容
  • 內容摘要用於摘錄和社交卡片
  • 內容分類和自動標籤
  • 智能搜尋理解意圖,而不只是關鍵詞
  • 草稿協助用於內容作者

設定 Anthropic SDK

npm install @anthropic-ai/sdk
// lib/claude.ts
import Anthropic from '@anthropic-ai/sdk'

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY!,
})

export async function generateSEOMetadata(content: string, title: string) {
  const message = await anthropic.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 1024,
    messages: [
      {
        role: 'user',
        content: `Given this article title and content, generate SEO metadata.

Title: ${title}
Content: ${content.slice(0, 3000)}

Respond with JSON only:
{
  "seo_title": "50-60 char title with primary keyword",
  "seo_description": "120-160 char meta description",
  "excerpt": "1-2 sentence hook for social sharing",
  "suggested_tags": ["tag1", "tag2", "tag3"]
}`,
      },
    ],
  })

  const text = message.content[0].type === 'text' ? message.content[0].text : ''
  return JSON.parse(text)
}

export async function classifyContent(content: string, existingTags: string[]) {
  const message = await anthropic.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 256,
    messages: [
      {
        role: 'user',
        content: `Classify this content into the most relevant tags from the existing list. You may suggest up to 2 new tags if nothing fits.

Existing tags: ${existingTags.join(', ')}

Content: ${content.slice(0, 2000)}

Respond with JSON: { "tags": ["tag1", "tag2"], "new_tags": ["maybe-new"] }`,
      },
    ],
  })

  const text = message.content[0].type === 'text' ? message.content[0].text : ''
  return JSON.parse(text)
}

AI 功能的 API 路由

// app/api/ai/seo/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { createClient } from '@/lib/supabase/server'
import { generateSEOMetadata } from '@/lib/claude'

export async function POST(request: NextRequest) {
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()

  if (!user) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const { content, title } = await request.json()
  
  try {
    const metadata = await generateSEOMetadata(content, title)
    return NextResponse.json(metadata)
  } catch (error) {
    return NextResponse.json(
      { error: 'AI generation failed' },
      { status: 500 }
    )
  }
}

這裡的成本可以忽略不計。一個典型的 SEO 元數據生成呼叫可能使用大約 4,000 個輸入令牌和 200 個輸出令牌。以 Claude Sonnet 4 的定價約 $3/100 萬輸入令牌和 $15/100 萬輸出令牌,那大約是每個呼叫 $0.015。你可以為 1,000 篇文章生成元數據,花費約 $15。

構建自訂管理介面

這是讓人們感到緊張的部分。「如果我沒有 CMS,非技術人員如何編輯內容?」

你構建一個簡單的管理 UI。在 2026 年,「簡單」實際上是簡單的。這是一個基本文章編輯器組件:

// app/(dashboard)/posts/[id]/editor.tsx
'use client'

import { useState } from 'react'
import { createBrowserClient } from '@supabase/ssr'

export function PostEditor({ post }: { post: Post }) {
  const [title, setTitle] = useState(post.title)
  const [body, setBody] = useState(post.body || '')
  const [saving, setSaving] = useState(false)
  
  const supabase = createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )

  async function save() {
    setSaving(true)
    const { error } = await supabase
      .from('posts')
      .update({
        title,
        body,
        updated_at: new Date().toISOString(),
      })
      .eq('id', post.id)

    setSaving(false)
    if (error) alert('Save failed: ' + error.message)
  }

  async function generateSEO() {
    const res = await fetch('/api/ai/seo', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title, content: body }),
    })
    const metadata = await res.json()
    // 應用生成的元數據到文章
    await supabase
      .from('posts')
      .update({ metadata, excerpt: metadata.excerpt })
      .eq('id', post.id)
  }

  return (
    <div className="max-w-4xl mx-auto p-6">
      <input
        type="text"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        className="text-3xl font-bold w-full mb-4 border-b pb-2"
      />
      <textarea
        value={body}
        onChange={(e) => setBody(e.target.value)}
        className="w-full h-96 font-mono text-sm p-4 border rounded"
      />
      <div className="flex gap-4 mt-4">
        <button onClick={save} disabled={saving}
          className="px-4 py-2 bg-blue-600 text-white rounded">
          {saving ? 'Saving...' : 'Save Draft'}
        </button>
        <button onClick={generateSEO}
          className="px-4 py-2 bg-purple-600 text-white rounded">
          ✨ Generate SEO Metadata
        </button>
      </div>
    </div>
  )
}

是的,這是一個簡單的 textarea。在一個真實的專案中,你會換上像 Tiptap、MDXEditor 或 BlockNote 這樣的東西用於富文本編輯。重點是:管理介面是你的代碼。你控制每個像素、每個工作流、每個權限。無需與 CMS UI 限制搏鬥。

對於更複雜的專案,考慮 Refine 或 AdminJS 作為直接連接到 Supabase 的管理面板框架。它們會為你節省數週。

真實成本:這個堆疊實際成本是多少

讓我們為一個內容豐富的網站做一些月度 10 萬次頁面瀏覽量的具體數字:

服務 層級 月度成本 你得到的
Vercel Pro $20 1TB 頻寬、1000 GB-小時無伺服器
Supabase Pro $25 8GB 資料庫、250GB 頻寬、10 萬 auth 用戶
Claude API 按使用量付費 ~$10-30 ~500 萬令牌/月(SEO 生成、摘要、搜尋)
域名 年度 ~$1 .com 域名
總計 $56-76/月

現在比較這與典型的 headless CMS 堆疊:

服務 層級 月度成本
Contentful Team $300
Vercel Pro $20
Algolia (搜尋) Build $50
Auth0 (認證) Essentials $35
總計 $405/月

那是 5-6 倍的成本差異。而且 Supabase 堆疊為你提供了更多靈活性,而不是更少。

何時仍應使用 CMS

我想對此有清楚的認識。不要為每個專案放棄你的 CMS。headless CMS 仍然是更好的選擇,當:

  • 大型編輯團隊需要結構化工作流(批准鏈、排程、超越基本 RBAC 的角色)
  • 內容是產品-- 發佈商、媒體公司、有數百名貢獻者的文檔網站
  • 你需要視覺編輯-- 某些 CMS 平台提供實時預覽和視覺建構者,複製需要幾個月
  • 多通道交付-- 如果相同的內容供應網站、行動應用、數字標牌和電子郵件,CMS 的結構化內容模型證明了其價值
  • 大規模本地化-- 像 Contentful 和 Sanity 這樣的 CMS 平台擁有成熟的 i18n 工作流

我們仍然在 Social Animal 構建大量 headless CMS 專案。如果那是你的專案需要的,與我們談論它。但對於越來越多的應用類別,其中它不是你需要的,停止為它付費。

生產部署檢查清單

在你將此堆疊運送到生產之前,運行此清單:

  • 行級安全性策略針對每個表進行測試(Supabase 的策略模擬器在這裡有幫助)
  • 速率限制 Claude API 路由(使用 Vercel 的 @vercel/edge 速率限制器或 upstash/ratelimit)
  • 輸入驗證在所有 API 路由上(Zod 是你的朋友)
  • 錯誤邊界在你的 React 樹中用於 AI 失敗(Claude 偶爾會超時)
  • 快取策略-- 在 Next.js 中對資料庫支持的頁面使用 unstable_cacherevalidateTag
  • 監控-- Vercel Analytics 用於性能、Supabase Dashboard 用於資料庫指標、Anthropic Console 用於 API 使用情況
  • 備份策略-- Supabase Pro 包括每日備份,但也設定邏輯複製或 pg_dump cron 以確保安心
  • 內容安全策略標頭在 next.config.js 中配置
  • 圖像最佳化-- 使用 Next.js <Image> 組件配合 Supabase Storage URL

常見問題

Supabase 真的能替代 headless CMS 嗎? 對於許多用例,是的。Supabase 為你提供一個 PostgreSQL 資料庫,其中包含一個 REST 和 GraphQL API 自動從你的模式生成、檔案儲存、認證和即時訂閱。它沒有提供的是一個開箱即用的優質內容編輯 UI -- 你需要自己構建它或使用像 Refine 這樣的工具。如果你的團隊是技術性的或規模很小,這個權衡絕對值得。

典型網站的 Claude API 成本是多少? 對於使用 Claude 進行 SEO 元數據生成、內容摘要和基本分類的內容網站,預計花費 $10-30/月(中等使用量,數百個 AI 操作)。Claude Sonnet 4 在 2026 年的定價大約是每 100 萬輸入令牌 $3,每 100 萬輸出令牌 $15。單個 SEO 元數據生成呼叫的成本大約 $0.01-0.02。

這個堆疊適合企業應用嗎? 這取決於你對企業的定義。Vercel 和 Supabase 都提供企業級層,具有 SLA、SOC 2 合規性和專屬支援。該堆疊可以很好地處理高流量 -- Next.js 在 Vercel 上自動擴展,Supabase Pro 支援連接池和讀副本。對於合規性重的行業,你會希望 Supabase 的自託管選項以將資料保留在你自己的基礎設施中。

沒有 CMS 的情況下如何處理內容預覽和草稿工作流? 你構建它們。Next.js Draft Mode 結合 posts 表中的 status 欄位為你提供草稿/已發布工作流。對於預覽,創建一個經過認證的路由,無論狀態如何都獲取文章。這大約是 50 行代碼,相比在 CMS 儀表板中配置預覽 URL。

在沒有 CMS 的情況下如何處理富文本編輯? 使用現代富文本編輯器庫。Tiptap(基於 ProseMirror 構建)是 2026 年最受歡迎的選擇 -- 它支援協作編輯、自訂塊、斜槓命令和 Markdown 快捷方式。BlockNote 是另一個不錯的選擇,具有 Notion 風格的 UI。將輸出存儲為 HTML、Markdown 或 JSON 在你的 Supabase body 欄位中。

我可以從 headless CMS 遷移到這個堆疊嗎? 絕對可以。大多數 headless CMS 平台都有匯出 API。編寫一個遷移指令碼,從你的 CMS API 拉取內容並將其插入 Supabase 表中。我們已經為幾個客戶進行了這個遷移,從 Contentful 和 Sanity 轉移到 Supabase 支持的設定。最困難的部分通常是將 CMS 的專有富文本格式對應到標準 HTML 或 Markdown。

如果 Supabase 宕機會怎樣? Supabase 在 2025-2026 年保持了良好的正常運行時間,但沒有服務是完美的。因為你的資料存放在標準 PostgreSQL 中,你有選項:設定讀副本、在 S3 中保留自動備份,或甚至運行備用實例。如果你在 Supabase 的自託管層上,你完全控制基礎設施。這實際上比依賴 CMS 廠商更有彈性 -- 如果 Contentful 出現故障,你不能只是「切換到另一個 Contentful」。

我應該對文章或行銷網站使用此堆疊嗎? 對於開發者的個人文章或初創公司的行銷網站,這個堆疊是完美的。你獲得完全控制、最少的成本和 AI 驅動的功能,在 CMS 中會需要昂貴的外掛。對於每週發佈 20+ 篇文章的大型行銷團隊,具有複雜的批准工作流,你可能會想要一個適當的 CMS。這是關於將工具與團隊匹配。如果你不確定哪種方法適合你的專案,請查看我們的定價頁面與我們聯繫進行快速諮詢。