WP WordPress Intermediate

Headless Starter Kit — Full guide

Install, configure and deploy Headless Starter Kit: WordPress plugin + turnkey Next.js 15 starter to move WooCommerce to headless.

Updated Module version 1.0.0

This guide covers full installation, configuration and usage of Headless Starter Kit — the WordPress plugin that turns your WooCommerce store into a headless commerce, shipped with a turnkey Next.js 15 starter.

Who this guide is for. Developers, agencies or technical merchants who want to move an existing WooCommerce site to headless without reinventing authentication, cart and checkout. You should be comfortable with the command line, Node.js, and a bit of server configuration if you go with Hetzner.

Overview

Headless Starter Kit ships as two deliverables in a single purchase:

  1. The WordPress plugin (ZIP uploaded via /wp-admin/plugins.php) which exposes on the backend: JWT authentication, cart API, checkout bridge, public config endpoint, ISR webhooks and strict CORS.
  2. The Next.js 15 starter (ZIP downloadable from the WordPress admin once the plugin is active and configured) which contains a complete Next.js project with home, listing, ISR product page, cart, checkout, login, register and customer account pages — environment variables already prefilled with your URLs and secrets.

The architecture is deliberately simple: your WooCommerce stays the source of truth (products, orders, stock, payments), and Next.js consumes the REST API through secure proxy routes. No database duplication, no replication to manage.

Requirements

WordPress side

  • WordPress 6.4 or above
  • WooCommerce 8.0 or above (tested up to 9.5)
  • PHP 8.1 or above
  • Permalinks set to Post name (not Plain)
  • HTTPS enabled (mandatory for httpOnly cookies and authentication in production)
  • A WooCommerce REST key pair with read/write access (generated in WooCommerce → Settings → Advanced → REST API)

Next.js frontend side

  • Node.js 20 or above (recommended: manage versions with nvm)
  • Node-compatible hosting: Vercel, Hetzner VPS, Netlify, Railway, or any server that can run Node 20+

Classic shared hosting (o2switch, shared OVH, etc.) is not suitable for hosting the Next.js frontend, which requires a persistent Node runtime. The WordPress plugin, however, runs on any WP-compatible host.

Installing the WordPress plugin

  1. From the WordPress back-office, go to Plugins → Add New → Upload Plugin.
  2. Select the dfheadlessstarterkit.zip file, then click Install Now.
  3. Click Activate Plugin.
  4. A new Headless Kit menu appears in the left sidebar, with three tabs: Settings, Diagnostics, Download starter.

On activation, the plugin automatically generates a random JWT secret and a revalidation token. You can regenerate them at any time from the settings.

Configuration

Open Headless Kit → Settings. Each section is independent and can be tweaked without restarting anything.

Frontend URL

Enter the full public URL of your Next.js app, without a trailing slash. Example:

https://shop.example.com

This URL serves three purposes: building the ISR webhooks, feeding the NEXT_PUBLIC_SITE_URL variable in the delivered starter, and validating the default CORS origin.

JWT secret and revalidation token

Two secrets are used:

  • JWT secret — signs access and refresh tokens. At least 32 characters. Never share it.
  • Revalidation token — sent in the Authorization: Bearer … header of ISR webhooks. Must match the REVALIDATE_TOKEN variable on the Next.js side.

A Regenerate button next to each field produces a cryptographically strong random secret via crypto.getRandomValues.

After regenerating the JWT secret, all existing access and refresh tokens become invalid. Users will have to log in again. Warn them or do it outside peak hours.

Cart mode: JWT or server?

The choice is made via a radio button in the settings.

JWT mode (default, recommended)

The full cart is serialised into a signed HS256 token and returned through the X-DFHSK-Cart header. No data is stored on the WordPress side. Ideal for:

  • Deployments on Vercel edge, Cloudflare, or multi-instance setups
  • High-traffic stores where avoiding the database on every call is a net win
  • Setups where WordPress is purely an API and doesn’t need sessions

Server mode (WC_Session)

The cart lives in WooCommerce’s native WC_Session table. Prefer this if:

  • You use WooCommerce extensions that hook into the cart (WooCommerce Subscriptions, Dynamic Pricing, YITH plugins, etc.)
  • You want to keep WooCommerce’s native session logic (native abandoned-cart follow-ups, server-side cross-sell, etc.)

CORS origins

A strict allow-list of origins allowed to call the API. One origin per line, full https://… format. Subdomain wildcards are supported:

https://shop.example.com
https://preview.example.com
https://*.previews.example.com
http://localhost:3000

Add http://localhost:3000 during development, then remove it in production.

ISR events

Five checkboxes indicate which WordPress events trigger an ISR webhook to Next.js:

  • Productssave_post_product, woocommerce_update_product
  • Categories — create, update, delete on the product_cat taxonomy
  • Orders — status changes (useful to refresh the account page)
  • Pagessave_post_page
  • Coupons — create and update on discount codes

A Paths to revalidate textarea lets you precisely define which Next.js paths are revalidated for each event (by default, the plugin intelligently infers the relevant paths).

Diagnostics

Headless Kit → Diagnostics. Eleven automated checks run every time the page is displayed:

  1. WooCommerce active — the WooCommerce class is available
  2. Pretty permalinks — the structure is not Plain
  3. REST API reachable/wp-json/ returns 200
  4. HTTPS enabledis_ssl() returns true
  5. WPGraphQL detected — info only, non-blocking
  6. JWT secret defined — at least 32 characters
  7. Frontend URL configured — non-empty and valid URL format
  8. CORS origins set — at least one origin
  9. Revalidation token defined — at least 24 characters
  10. WooCommerce REST keys — the plugin detects an active consumer_key/consumer_secret pair
  11. Cart mode readable — the chosen storage works

Each check displays green (OK), amber (warning, non-blocking) or red (blocking). Fix everything red before going to production.

Downloading and running the Next.js starter

Once the settings are filled and diagnostics are green, open Headless Kit → Download starter. Click the big Download Next.js starter button.

The delivered ZIP is a complete Next.js 15 project, generated on the fly with your URLs and secrets already injected. Placeholders replaced at generation time:

  • {{SITE_URL}} → your WordPress URL
  • {{FRONTEND_URL}} → the configured frontend URL
  • {{REVALIDATE_TOKEN}} → your revalidation token
  • {{CURRENCY}} → WooCommerce currency
  • {{SITE_NAME}} → site title
  • {{LOCALE}} → WordPress locale (en, fr, es, etc.)

The .envtmpl file is renamed to .env.example at generation.

Environment variables to fill in manually

Some values can’t be retrieved automatically — you must add them to the .env file (create it from .env.example):

WOO_REST_CONSUMER_KEY=ck_xxxxxxxxxxxxxx
WOO_REST_CONSUMER_SECRET=cs_xxxxxxxxxxxxxx
SESSION_PASSWORD=your-32-character-minimum-password

Generate a strong SESSION_PASSWORD:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Local development

npm install
cp .env.example .env
# Edit .env with your missing secrets
npm run dev

The frontend is served on http://localhost:3000. Add this URL to CORS origins on WordPress during development.

Deploying to Vercel

npx vercel link
npx vercel env pull .env.production
npx vercel deploy --prod

Then configure every environment variable in the Vercel dashboard (Project → Settings → Environment Variables). The delivered vercel.json file pins the region to cdg1 (Paris) and disables caching on /api/* routes.

Deploying to Hetzner (or any Ubuntu/Debian VPS)

# On the server, as root or with sudo
git clone your-repo.git storefront && cd storefront
cp .env.example .env
nano .env                       # Fill in all variables
bash deploy/hetzner.sh

The script installs Docker if needed, builds the image, launches the container and exposes the service on 127.0.0.1:3000. Add Caddy or nginx as a reverse-proxy for TLS. Minimal Caddyfile example:

shop.example.com {
  encode zstd gzip
  reverse_proxy 127.0.0.1:3000
}

Exposed REST endpoints

All plugin endpoints live under the dfhsk/v1 namespace. Full URL: https://example.com/wp-json/dfhsk/v1/…

Authentication

  • POST /auth/login — body: { username, password } → returns { token, refresh_token, user }
  • POST /auth/refresh — body: { refresh_token } → returns a new token
  • GET /auth/me — header: Authorization: Bearer … → returns the current user
  • POST /auth/register — body: { email, password, first_name, last_name }
  • POST /auth/logout — invalidates the refresh token

Cart

Every call passes the cart token via the X-DFHSK-Cart header. The server returns a new token in the same header on every response.

  • GET /cart — full snapshot (items, totals, taxes, shipping)
  • POST /cart/add — body: { product_id, quantity, variation? }
  • POST /cart/update — body: { key, quantity }
  • POST /cart/remove — body: { key }
  • POST /cart/coupon — body: { code }
  • DELETE /cart/coupon/{code}
  • POST /cart/shipping — body: { country, postcode } → returns applicable rates
  • POST /cart/clear

Checkout

  • POST /checkout/create-order — body: { payment_method, billing, shipping? } → returns { order_id, order_key, redirect }. The redirect is the URL to the payment gateway (Stripe, PayPal, etc.).
  • GET /checkout/order/{id}Authorization: Bearer … header required

Public configuration

  • GET /config — no authentication needed. Returns { currency, base_country, countries, payment_methods, tax_settings }. The Next.js starter hits this endpoint to hydrate the checkout forms.

ISR webhooks (Next.js side)

The plugin sends POST requests to {FRONTEND_URL}/api/revalidate with the header Authorization: Bearer {REVALIDATE_TOKEN}. The body is JSON:

{
  "paths": ["/products/linen-jacket", "/products"],
  "tags": ["product:linen-jacket"],
  "reason": "wc_update_product"
}

The /api/revalidate route delivered in the starter validates the token then calls revalidatePath and revalidateTag for each entry.

Customising the starter

The delivered code is licensed under GPL v2 — you can modify, extend, redistribute without restriction. Common entry points:

  • Colour palette: tailwind.config.ts, brand palette (orange by default)
  • Shop components: src/components/shop/ (Header, Footer, ProductCard, CartProvider)
  • Public pages: src/app/(shop)/
  • Customer account pages: src/app/(auth)/
  • Price and date formatting: src/lib/format.ts
  • TypeScript types: src/types/woo.ts
  • API call helper: src/lib/woo-rest.ts (wooRest and dfhskFetch functions)

To add a new page listing say a brand’s products, duplicate src/app/(shop)/products/page.tsx and adapt the WooCommerce REST query. The wooRest<T>() function handles Basic authentication automatically.

Troubleshooting

CORS error in the browser console

The frontend origin isn’t in the allow-list. Add it under Settings → CORS origins, one per line, without a trailing slash.

ISR webhooks return 401

The Next.js REVALIDATE_TOKEN doesn’t match the token configured on WordPress. Copy the exact value from WP settings into the Next.js .env, then redeploy.

Login returns 403 despite correct credentials

Check that the user account has a password set (not social-only login), that HTTPS is active in production, and that the JWT secret is at least 32 characters. Check the Diagnostics tab.

The cart empties between two pages

In JWT mode, verify the starter reads and writes the token in localStorage (key dfhsk_cart_token). Open the browser inspector → Application → Local Storage.

An order created in WooCommerce doesn’t show up in Next.js

Check that the Orders event is enabled in ISR events and that the webhook goes through without error (enable WP_DEBUG_LOG).

Going further

The starter is a starting point, not a finished product. Depending on your project, consider adding:

  • An instant search engine (Algolia, Meilisearch, Typesense) fed by the same ISR webhooks
  • Editorial content management on WordPress with ACF or similar, plus a dedicated Next.js page
  • A PWA with a service worker for offline mode (see also our dfpwa module)
  • Real-time AI personalisation (see dfsmartcontent)

For technical questions, contact DataFirefly support with your logs and the results from the Diagnostics tab.

Was this page helpful?

Still stuck? Contact support