Back to Blog
Tutorial11 min read2026-07-05

How to Deploy Headless WordPress as a CMS

Headless WordPress keeps the editor everyone loves while serving content through an API to a modern frontend. This tutorial covers the architecture, REST vs WPGraphQL, and deploying the pieces with a managed database.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

# How to Deploy Headless WordPress as a CMS

WordPress still powers a huge share of the web, and editors love its admin experience. But its default PHP-rendered themes feel dated next to a React, Next.js, or Astro frontend. Headless WordPress is the compromise: keep WordPress as the content backend and editing interface, but serve content through an API to a decoupled frontend. This tutorial covers the architecture and deployment.

The architecture

Editors → WordPress admin (PHP + MySQL)
                │  exposes content via REST or GraphQL
                ▼
        Frontend (Next.js / Astro / etc.) → visitors

Two deployable concerns:

  1. 1WordPress backend — PHP application + a MySQL database. Used by editors and as the content API. Often locked down so the public never hits it directly.
  2. 2Frontend — a modern app that fetches content at build time (SSG) or request time (SSR/ISR) and renders it.

Step 1: choose your API — REST vs WPGraphQL

WordPress ships a built-in REST API. The popular alternative is the WPGraphQL plugin.

WP REST APIWPGraphQL
SetupBuilt inInstall plugin
Over/under-fetchingCommon (fixed shapes)Request exactly what you need
Nested dataMultiple requestsSingle query
Learning curveLowGraphQL knowledge

For a content-heavy site with related data (posts → authors → categories → media), WPGraphQL usually means fewer round trips. For simple needs, the built-in REST API is fine and requires no plugins.

# REST: fetch published posts
curl https://cms.example.com/wp-json/wp/v2/posts?status=publish
# WPGraphQL: exactly the fields you need, in one query
query { posts { nodes { title slug author { node { name } } } } }

Step 2: containerize WordPress

Use the official WordPress image and drive config via environment variables so no secrets live in the image:

FROM wordpress:php8.2-apache
# Add plugins/themes via COPY or a build step as needed
COPY ./wp-content/plugins /var/www/html/wp-content/plugins

WordPress reads its database config from environment variables:

WORDPRESS_DB_HOST=<managed-mysql-host>
WORDPRESS_DB_USER=<user>
WORDPRESS_DB_PASSWORD=<password>
WORDPRESS_DB_NAME=wordpress
WORDPRESS_CONFIG_EXTRA=define('WP_HOME','https://cms.example.com');

Step 3: provision MySQL

WordPress needs MySQL. Provision a managed MySQL (5.7 or 8.x), create the database, and point WordPress at it via the env vars above. Managed MySQL gives you backups and a stable host instead of running the DB inside the same container (which would lose data on restart).

Step 4: handle media (the stateful gotcha)

The biggest headless-WordPress deployment trap is uploads. By default WordPress writes media to wp-content/uploads on local disk — which vanishes when a container restarts or reschedules. Two fixes:

  • Offload media to object storage (S3-compatible) via a plugin, so uploads live outside the container.
  • Or attach persistent storage to the WordPress container.

For a cloud-native deploy, offloading media to object storage is the cleaner approach.

Step 5: lock down the backend

In a headless setup, visitors should hit your frontend, not WordPress directly. Hardening steps:

  • Restrict the WordPress admin to known IPs or behind SSO where possible.
  • Disable the default themes/front-end rendering you do not need.
  • Cache the API responses aggressively at the edge.
  • Keep WordPress and plugins updated — it is a frequent attack target.

Step 6: build the frontend

Your frontend fetches from the WordPress API. With static generation, content is baked at build time for maximum speed:

// Next.js: fetch posts at build
export async function getStaticProps() {
  const res = await fetch('https://cms.example.com/wp-json/wp/v2/posts');
  const posts = await res.json();
  return { props: { posts }, revalidate: 60 }; // ISR: refresh hourly-ish
}

Use Incremental Static Regeneration (or scheduled rebuilds) so new content appears without a full redeploy.

Common pitfalls

  • Local uploads lost on restart. Offload media to object storage.
  • DB inside the container. Use a managed MySQL so data survives.
  • WordPress URL mismatch. Set WP_HOME/WP_SITEURL correctly or you get redirect loops.
  • Exposing the backend publicly unprotected. Headless WP is still WordPress — harden it.
  • CORS. Allow your frontend origin if it calls the API from the browser.

Deploying on PandaStack

PandaStack builds containers from any Dockerfile, so the official WordPress image deploys directly. Provision a managed MySQL (5.7 or 8.x) for content and set the WORDPRESS_DB_* env vars — PandaStack manages the database with scheduled and manual backups, so your content is protected. Deploy your frontend as a static site (Next export, Astro, etc.) or a container; PandaStack auto-detects the framework and builds it. Custom domains get automatic SSL on both the CMS and the frontend, and edge functions or cronjobs can trigger rebuilds when content changes. Remember the media gotcha: offload uploads to object storage rather than container disk. The free tier (5 web services + 5 static sites + 1 database) can host both halves of a headless WordPress setup.

References

  • [WordPress REST API handbook](https://developer.wordpress.org/rest-api/)
  • [WPGraphQL documentation](https://www.wpgraphql.com/docs/introduction)
  • [Official WordPress Docker image](https://hub.docker.com/_/wordpress)
  • [WordPress hardening guide](https://developer.wordpress.org/advanced-administration/security/hardening/)

---

Headless WordPress keeps editors happy while giving you a modern frontend — the deploy is mostly about media and database persistence. PandaStack builds both halves and provides managed MySQL with backups — start free at [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also