Architecture

Headless CMS Architecture: How It Works Under the Hood

Jay Callicott··9 min read

Headless CMS Architecture: How It Works Under the Hood

Every headless CMS makes the same fundamental promise: separate content from presentation. But what does that actually look like at the systems level? Where does the content live, how does it get to your frontend, and what happens between the API response and the rendered page?

This article walks through headless CMS architecture layer by layer — from the content repository to the CDN edge — so you can make informed decisions about how to structure your next project. If you're new to the concept entirely, start with What Is a Headless CMS? first.

The Core Architecture

At its simplest, a headless CMS has three layers: a content repository where structured data lives, an API layer that exposes that data, and one or more frontends that consume it. Here's the high-level view:

+---------------------+
|     FRONTENDS       |
|                     |
|  Next.js   Mobile   |
|  Astro     IoT      |
|  Nuxt      Kiosk    |
+--------+------------+
         |
         | HTTP / HTTPS
         |
+--------v------------+
|     API LAYER       |
|                     |
|  REST   GraphQL     |
|  JSON:API           |
|                     |
|  Auth / Rate Limits |
+--------+------------+
         |
+--------v------------+
|  CONTENT REPOSITORY |
|                     |
|  Content Types      |
|  Fields & Relations |
|  Media Assets       |
|  Revisions          |
|  User Permissions   |
+---------------------+

This is the diagram you'll see in every headless CMS pitch deck. But the real architecture is more nuanced. Let's break each layer down.

Layer 1: The Content Repository

The content repository is the persistence layer — the database, file storage, and content modeling system that holds your structured data.

A well-designed content repository provides:

Typed content modeling. Content types (or "models" or "schemas" depending on the platform) define the shape of your data. A "Blog Post" content type might have fields for title (text), body (rich text), author (reference to a Person), publish date (datetime), and hero image (media). These types enforce structure at the data layer, not at the presentation layer.

Entity relationships. Content rarely exists in isolation. Articles reference authors, products reference categories, pages reference reusable components. The content model must support typed references between entities — ideally with referential integrity, so you can't delete an author while their articles still exist.

Revision history and workflows. Production content systems need editorial controls: draft/published states, revision history, content moderation queues, and role-based access. This is where enterprise CMS platforms like Drupal distinguish themselves from lighter tools — Drupal's content moderation system supports custom editorial workflows with configurable states and transitions out of the box.

Media management. Images, videos, and documents need their own storage, processing pipeline (resizing, format conversion, CDN delivery), and metadata tracking.

Layer 2: The API

The API layer is the contract between your content and every system that consumes it. The choice of API protocol shapes how your frontends request data, how much data travels over the wire, and how cacheable your responses are.

API Protocol Comparison

+-------------------+-------------------+-------------------+
|       REST        |     GraphQL       |     JSON:API      |
+-------------------+-------------------+-------------------+
| Fixed endpoints   | Single endpoint   | Specification-    |
| per resource      | with flexible     | driven REST with  |
|                   | queries           | includes, filters |
| /api/articles     |                   | sparse fieldsets  |
| /api/articles/42  | POST /graphql     |                   |
|                   | { query { ... } } | /jsonapi/node/    |
| Simple caching    |                   |   article?include |
| (HTTP native)     | Precise data      |   =field_author   |
|                   | fetching          |                   |
| Over-fetching     | Complex caching   | Standardized      |
| is common         | (needs client)    | pagination,       |
|                   |                   | filtering, sorts  |
| Best for simple   | Best for complex  |                   |
| CRUD              | frontend needs    | Best for typed    |
|                   |                   | resource APIs     |
+-------------------+-------------------+-------------------+

REST is straightforward and cache-friendly. Each resource gets an endpoint, responses are highly cacheable via HTTP headers, and every developer already knows the conventions. The downside: you often over-fetch (getting 30 fields when you need 5) or under-fetch (requiring multiple requests to assemble a page).

GraphQL solves the over-fetching problem by letting clients request exactly the fields they need in a single query. This is particularly valuable for mobile clients with bandwidth constraints or complex pages that pull from many content types. The trade-off is caching complexity — since every query is a POST to a single endpoint, HTTP-level caching doesn't work natively. You'll need a client-side cache (Apollo, urql) or a CDN that understands GraphQL. For implementation details, see our GraphQL documentation.

JSON:API is a specification for building REST APIs that addresses over-fetching without abandoning REST conventions. It supports sparse fieldsets (request only the fields you need), includes (embed related entities in a single response), and standardized filtering and pagination. Drupal core ships with a JSON:API module, making it one of the most mature JSON:API implementations available. See our API documentation for working examples.

Authentication and Access Control

The API layer handles more than data retrieval. It must enforce:

  • Authentication — API keys for server-to-server calls, OAuth tokens for user-context requests, JWT for stateless session management
  • Authorization — role-based access to content (editors see drafts, anonymous users see published content only)
  • Rate limiting — protecting the backend from abusive clients or runaway build processes
  • CORS policies — controlling which frontend domains can make browser-based API requests

Layer 3: The Delivery Layer

This is where headless architecture gets interesting — and where most of the complexity lives. The delivery layer includes your CDN, your frontend build process, and the rendering strategy that determines when HTML gets generated.

The Full Delivery Pipeline

+-----------+     +-----------+     +-----------+     +----------+
|           |     |           |     |           |     |          |
|  Content  +---->+  Webhook  +---->+  Build /  +---->+   CDN    |
|  Updated  |     |  Trigger  |     |  Render   |     |  Edge    |
|           |     |           |     |           |     |          |
+-----------+     +-----------+     +-----------+     +----+-----+
                                                           |
                                                      +----v-----+
                                                      |          |
                                                      |  User's  |
                                                      |  Browser |
                                                      |          |
                                                      +----------+

Webhooks are the bridge between content changes and frontend updates. When an editor publishes a page, the CMS fires an HTTP callback to your build system — triggering a rebuild, cache invalidation, or on-demand revalidation. Without webhooks, your frontend would need to poll the API for changes or accept stale content. Our webhooks documentation covers how to configure these triggers for common deployment targets.

CDN caching is what makes headless architecture performant at scale. Once content is rendered (whether at build time or request time), the output is cached at CDN edge nodes worldwide. Subsequent requests for the same page hit the edge, not the CMS. This means your content repository can be modest in resources while your site handles millions of requests — the CDN does the heavy lifting.

Rendering Strategies: SSG vs SSR vs ISR

The rendering strategy you choose determines the trade-off between content freshness and performance:

Strategy    Build Time     First Request    Content Update
------------------------------------------------------------
SSG         Slow (all      Instant (CDN)    Requires full
            pages built)                    rebuild

SSR         None           Slower (server   Immediate
                           renders)         (always fresh)

ISR         Fast (on       First hit may    Revalidates on
            demand)        be stale         schedule or
                                            on-demand

Static Site Generation (SSG) renders every page at build time. Pages are pure HTML on the CDN — no server required at request time. The problem: for sites with thousands of pages, builds can take minutes, and content updates require a full rebuild cycle. Frameworks like Astro and Next.js handle SSG natively.

Server-Side Rendering (SSR) generates HTML on every request. Content is always fresh, but every page hit requires a server round-trip to the CMS API. This adds latency and requires always-on compute. Good for personalized or highly dynamic content.

Incremental Static Regeneration (ISR) is the middle ground, popularized by Next.js. Pages are statically generated but revalidated on a schedule (e.g., every 60 seconds) or on-demand via webhook. This gives you CDN performance with near-real-time content updates. For most headless CMS projects, ISR is the right default.

Multi-Channel Delivery

The architectural payoff of separating content from presentation is multi-channel delivery. The same content repository serves every consumer:

                        +---> Next.js Website
                        |
                        +---> React Native Mobile App
                        |
Content Repository ---->+---> Email Newsletter (MJML)
       (API)            |
                        +---> In-Store Kiosk (Electron)
                        |
                        +---> AI Agent (MCP / LLM)
                        |
                        +---> Voice Assistant (Alexa Skill)

This only works if your content model is presentation-agnostic. If your "body" field contains HTML with CSS classes, your mobile app is stuck parsing markup it can't render. Well-architected headless content uses structured fields — separate fields for title, summary, body (as portable rich text or markdown), and media — rather than WYSIWYG blobs.

Architecture Patterns Compared

Not every project needs a fully headless architecture. Here's how the three main patterns compare:

Monolithic (Traditional CMS)

+---------------------------------------+
|            Monolithic CMS             |
|                                       |
|  Content DB + Templates + Rendering   |
|                                       |
|  Tightly coupled. One deployment.     |
|  Content editors and developers       |
|  work in the same system.             |
+---------------------------------------+
          |
          v
      HTML to Browser

Best for: Simple sites where the CMS team owns the full stack. WordPress, traditional Drupal, Joomla.

Limitation: You can't easily reuse content outside the CMS-rendered templates. Adding a mobile app means building a separate API after the fact.

Fully Headless (Decoupled)

+-------------------+     +-------------------+
|   Content Repo    |     |    Frontend(s)    |
|   (CMS Backend)   +---->+    (Any Stack)    |
|                   | API |                   |
|   Separate team,  |     |   Separate team,  |
|   separate deploy |     |   separate deploy |
+-------------------+     +-------------------+

Best for: Teams that need multi-channel delivery, frontend flexibility, or are using a modern JavaScript framework. This is the architecture most headless CMS platforms (Contentful, Sanity, Decoupled.io) follow.

Limitation: Two systems to manage, two deployments to coordinate, and the two-stack problem when your backend and frontend run different technology ecosystems.

Hybrid (Progressively Decoupled)

+---------------------------------------+
|            CMS Backend                |
|                                       |
|  Content DB + API + Optional SSR      |
|                                       |
|  Renders some pages server-side,      |
|  exposes API for JavaScript-heavy     |
|  sections or external consumers.      |
+---------------------------------------+
          |              |
          v              v
    Server HTML     API Consumers
    (fallback)      (React, Mobile)

Best for: Teams migrating from a monolith that want to decouple incrementally. Drupal supports this natively — you can use its built-in rendering for some routes while exposing JSON:API or GraphQL for others.

The Two-Stack Reality

The fully headless pattern introduces an operational challenge that's often glossed over in architecture diagrams: you're running two technology stacks. The CMS backend (often PHP, Ruby, or Python) and the frontend (Node.js, typically) have different dependency trees, different deployment pipelines, and different scaling characteristics.

For teams building on open-source CMS backends like Drupal, this means maintaining PHP infrastructure (web server, database, caching, background queues) alongside a Node.js deployment for your Next.js or Astro frontend. We've written about this in detail in The Two-Stack Problem.

How Managed Platforms Solve It

The managed headless CMS approach — whether it's Contentful abstracting away the backend entirely, or Decoupled.io managing Drupal infrastructure for you — eliminates the backend operations burden.

With Decoupled.io, the architecture looks like this:

+---------------------------+     +-------------------+
|   Decoupled.io (Managed)  |     |   Your Frontend   |
|                           |     |                   |
|   Drupal Backend          |     |   Next.js, Astro, |
|   Auto-scaled             +---->+   React Native,   |
|   Security patched        | API |   or any client   |
|   Core updates handled    |     |                   |
|   GraphQL + JSON:API      |     |   You deploy this |
+---------------------------+     +-------------------+
     You don't manage this           on Vercel, etc.

You get Drupal's mature content modeling, editorial workflows, and open-source data ownership — without managing PHP infrastructure, applying security patches, or coordinating backend deployments. Your team focuses exclusively on the frontend and content.

Caching Architecture in Practice

For production headless CMS deployments, caching happens at multiple levels:

Request Flow:

Browser --> CDN Edge --> (cache miss?) --> App Server --> (cache miss?) --> CMS API
   ^          ^                               ^
   |          |                               |
   |     Edge Cache                    Response Cache
   |     (Vercel, CF)                  (Redis, in-memory)
   |
Browser Cache
(Cache-Control headers)

Browser cache — controlled by Cache-Control and ETag headers. Repeat visitors skip the network entirely for cached assets.

CDN edge cache — platforms like Vercel, Cloudflare, and Netlify cache rendered pages at edge locations. Cache invalidation happens via purge APIs triggered by CMS webhooks.

Application-level cache — your frontend server (if using SSR) can cache CMS API responses in Redis or memory to avoid hitting the CMS on every request.

CMS-level cache — the CMS itself caches database queries and rendered API responses. Drupal's cache tag system is particularly sophisticated — it can invalidate exactly the cached responses that reference a changed entity, without purging unrelated caches.

Effective caching can reduce CMS API traffic by 95% or more in production, which is why headless architecture scales well even with modest backend resources.

Choosing the Right Architecture

The right headless CMS architecture depends on your constraints:

Choose monolithic if your team is small, your delivery channel is a single website, and you don't anticipate needing API-driven content delivery. There's no shame in a well-run WordPress or Drupal monolith.

Choose fully headless if you need multi-channel delivery, your frontend team wants full control over the rendering stack, or you're building with AI app builders like Lovable or Bolt.new that need a content API backend.

Choose hybrid/progressive if you're migrating from a monolith and want to decouple incrementally, or if some parts of your site benefit from server rendering while others need a rich JavaScript frontend.

Choose managed headless if you want the architectural benefits of headless without the operational burden of running backend infrastructure. This is the gap Decoupled.io fills — managed Drupal providing the content engine, with your team owning the frontend.

What to Build Next

If you're ready to implement a headless CMS architecture, start with the API layer — it's the contract that everything else depends on. Explore our GraphQL and JSON:API documentation to see how content gets delivered, then set up webhooks to connect content changes to your frontend build pipeline.

The architecture diagrams above are a starting point. Every production system adds complexity — authentication flows, preview environments, multi-environment content promotion, localization. But the core pattern holds: structured content in, API out, render anywhere.