N
HeadlessCoreHeadless Core
Back to Home

Security Guide

HeadlessCore is designed for self-hosted headless WordPress. Security is built into the theme and Next.js starter kit, but production hardening is a shared responsibility — unlike WP Engine's managed platform, you control the infrastructure.

Production deploy: aaru.live runbook (Cloudflare + WP + Next.js).

This guide covers:

  1. Built-in security controls (what ships in the repo)
  2. Production hardening checklist
  3. Parallel comparison with WP Engine + Faust.js
  4. Configuration reference

Defense in depth

                    ┌─────────────────────────────────────┐
                    │  Edge (Cloudflare / CDN / WAF)      │  ← You configure
                    └──────────────────┬──────────────────┘
                                       │
                    ┌──────────────────▼──────────────────┐
                    │  Next.js frontend (public site)      │
                    │  • CSP + security headers           │
                    │  • Secret-gated API routes          │
                    │  • Rate limits (+ optional Redis)   │
                    │  • Turnstile CAPTCHA on forms       │
                    └──────────────────┬──────────────────┘
                                       │ WPGraphQL (HTTPS)
                    ┌──────────────────▼──────────────────┐
                    │  WordPress CMS (private subdomain)  │
                    │  • Standard WP auth (wp-admin)      │
                    │  • GraphQL mutation rate limits     │
                    │  • Introspection off in production  │
                    │  • XML-RPC disabled, no generator   │
                    └─────────────────────────────────────┘

Principle: The public site never runs PHP. WordPress admin and the database stay off the main marketing domain (aaru.live), ideally on a separate subdomain (cms.aaru.live) with restricted access.


Built-in controls

Next.js frontend

Control Where Details
Security headers next.config.ts X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, HSTS (production), CSP
Revalidation secret /api/revalidate Shared secret in JSON body; constant-time compare (secureCompare)
Preview secret /api/preview Query-param gate; constant-time compare
Rate limiting API routes 30/min revalidate, 20/min preview, 10/min downloads; in-memory or Upstash Redis
Form protection Contact, newsletter, comments Honeypot field, Turnstile CAPTCHA, per-IP rate limits
Search indexing POST /api/search SEARCH_INDEX_WEBHOOK_SECRET + rate limit
Stripe webhooks /api/stripe-webhook Signature verification via stripe.webhooks.constructEvent
License keys hc1. / hnp1. Offline RSA-2048 verification; no license phone-home
License delivery WordPress mutation hash_equals on LICENSE_DELIVERY_SECRET
Theme downloads /api/theme-updates/download Valid license required; ZIPs outside public/
Multi-tenant tenant.ts Per-tenant revalidation/preview secrets when enabled

WordPress theme

Control Where Details
GraphQL introspection inc/security.php Disabled when WP_ENVIRONMENT_TYPE=production
Public mutation rate limits inc/security.php Contact, newsletter, comments, search log — per-IP transients
XML-RPC inc/security.php Disabled
User enumeration inc/security.php Blocks ?author= for anonymous visitors
Generator tag inc/security.php WordPress version hidden from HTML
Security headers inc/security.php nosniff, SAMEORIGIN, HSTS on HTTPS
File editor inc/security.php DISALLOW_FILE_EDIT unless already set in wp-config.php
Akismet inc/akismet.php Optional comment spam when plugin active
AI mutations inc/ai-tools.php Requires valid pro license and edit_posts
License verification functions.php openssl_verify() on every uncached check

Production hardening checklist

Use this before going live (e.g. aaru.live + cms.aaru.live).

Infrastructure

  • HTTPS everywhere — TLS on frontend and CMS; redirect HTTP → HTTPS
  • Separate domains — Public site on www / apex; CMS on cms. subdomain
  • Cloudflare (or similar) — WAF, DDoS protection, bot fight mode on CMS
  • Restrict CMS access — Cloudflare Access, IP allowlist, or VPN for /wp-admin
  • Hide CMS from searchrobots.txt disallow on cms subdomain; no public links

Secrets (generate with openssl rand -hex 32)

  • REVALIDATION_SECRET — match in WP Headless Core → Settings
  • PREVIEW_SECRET — long random string
  • SEARCH_INDEX_WEBHOOK_SECRET — if using pluggable search
  • LICENSE_DELIVERY_SECRET — if using Stripe license delivery
  • TURNSTILE_SECRET_KEY + NEXT_PUBLIC_TURNSTILE_SITE_KEY — production forms
  • Never commit .env or PEM private keys

WordPress (wp-config.php)

define( 'WP_ENVIRONMENT_TYPE', 'production' );
define( 'DISALLOW_FILE_EDIT', true );
define( 'FORCE_SSL_ADMIN', true );
// Unique keys from https://api.wordpress.org/secret-key/1.1/salt/
  • Strong admin password + 2FA plugin (e.g. Wordfence, Solid Security)
  • Remove default admin username
  • Keep WordPress, PHP, and plugins updated
  • Disable unused plugins; only WPGraphQL + JWT auth required

Next.js

  • Set NODE_ENV=production
  • Configure Upstash Redis for distributed rate limits (see below)
  • Review CSP in next.config.ts if adding third-party scripts
  • Deploy latest build (theme-updates, /download, webhooks must exist)

Optional but recommended

  • Fail2ban or Cloudflare rate rules on CMS login
  • Database backups (daily) + off-site storage
  • Uptime monitoring on frontend and GraphQL endpoint
  • Security headers scan (securityheaders.com)

Distributed rate limiting (Upstash)

In-memory rate limits only apply per Node.js process. On Vercel, Railway, or multiple containers, configure Upstash (free tier available):

UPSTASH_REDIS_REST_URL=https://xxxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token

When both are set, /api/revalidate, /api/preview, /api/search (POST), and download routes use Redis automatically. If Upstash is unreachable, the app falls back to in-memory limits and logs a warning.


HeadlessCore vs WP Engine + Faust.js

Parallel comparison for agencies evaluating both stacks.

Platform model

Aspect HeadlessCore WP Engine Headless Platform + Faust.js
Hosting Self-hosted (any VPS, Vercel, Coolify) WP Engine managed WP + managed Node.js
Cost Server cost + optional $99 license ~$20–50+/mo per environment (platform fees)
Vendor lock-in None (MIT core, standard WPGraphQL) Medium (platform deploy, Faust tooling)
WordPress host Any (XAMPP, DigitalOcean, etc.) WP Engine required for integrated experience
Frontend Next.js 16 App Router (you own) Faust.js toolkit on Next.js (evolving to modular HWP Toolkit)

Security

Aspect HeadlessCore WP Engine + Faust.js
Managed WAF / DDoS You add (Cloudflare, etc.) Included on platform
WP security patching Your responsibility Automatic on WP Engine
SOC2 / enterprise compliance Your infrastructure WP Engine certified
Decoupled attack surface Yes — no PHP on public site Yes — same headless benefit
Preview authentication Shared secret (PREVIEW_SECRET) OAuth/JWT redirect flow (more standard)
Revalidation Shared secret webhook Platform / Faust-integrated
GraphQL introspection Off in production (theme) Configurable (your WPGraphQL settings)
Form spam protection Turnstile + honeypot + rate limits Bring your own
Rate limiting Built-in (+ optional Redis) Platform edge limits + your code
License / entitlement Offline RSA (hc1.) — no telemetry N/A (subscription billing)
Security headers (frontend) CSP, HSTS, X-Frame-Options in next.config.ts Your Faust/Next.js config
CMS hardening Theme hooks (inc/security.php) WP Engine hardening + your config

Features (not security, but affects product choice)

Feature HeadlessCore WP Engine + Faust.js
Pro Gutenberg blocks 47 blocks built-in DIY / ACF blocks
WooCommerce headless Store API checkout Possible, DIY
Multi-tenant Optional built-in No
i18n (Polylang/WPML) Built-in DIY
Search (Meilisearch/Algolia) Pro built-in DIY
AI content tools Pro built-in No
CRM (HubSpot/Mailchimp) Pro built-in DIY
Stripe license delivery Built-in No
Theme auto-updates License-gated self-host Git deploy

Verdict

Scenario Better fit
Enterprise team, compliance budget, no DevOps WP Engine
Agency resale, white-label, many client sites HeadlessCore
Maximum control, lowest long-term cost HeadlessCore
Preview/auth DX out of the box Faust.js (OAuth flow)
Feature-rich product bundle HeadlessCore
Security with zero configuration WP Engine
Security with Cloudflare + this checklist Comparable

Honest summary: WP Engine wins on managed infrastructure security by default. HeadlessCore can reach parity or better control when you deploy with Cloudflare WAF, Upstash rate limits, hardened wp-config.php, and CMS access restrictions — at the cost of more setup.


API route security reference

Route Auth Rate limit
POST /api/revalidate REVALIDATION_SECRET 30/min/IP
GET /api/preview PREVIEW_SECRET 20/min/IP
POST /api/search SEARCH_INDEX_WEBHOOK_SECRET 60/min/IP
GET /api/search Public (CAPTCHA N/A) 60/min/IP
POST /api/stripe-webhook Stripe signature None (Stripe retries)
GET /api/theme-updates/* License key 10/min/IP (download)
GET /api/download/product Stripe session or hc1. key 10/min/IP
Contact/newsletter server actions Turnstile + honeypot 5/15min/IP

See API Routes for request/response formats.


Stripe licensing tradeoff

Automated license delivery stores LICENSE_SIGNING_PRIVATE_KEY_PEM on the Next.js server. This is opt-in — manual CLI key generation keeps the private key fully offline. See Stripe Licensing and Licensing.


Reporting vulnerabilities

If you discover a security issue in HeadlessCore, report it privately to hello@aarudhnextgen.com rather than opening a public GitHub issue. Include steps to reproduce and affected versions.


Related docs