# Headless Starter Kit — Full guide

> Full install, configuration and deployment guide for Headless Starter Kit: the WordPress plugin that turns your WooCommerce store into a headless commerce, shipped with a preconfigured Next.js 15 starter.

- Page: <https://www.datafirefly.com/en/documentation/headless-starter-kit/>
- Language: en
- Last updated: 2026-07-01
- Other languages: [fr](https://www.datafirefly.com/documentation/headless-starter-kit/index.md), [es](https://www.datafirefly.com/es/documentation/headless-starter-kit/index.md), [de](https://www.datafirefly.com/de/documentation/headless-starter-kit/index.md), [it](https://www.datafirefly.com/it/documentation/headless-starter-kit/index.md), [pl](https://www.datafirefly.com/pl/documentation/headless-starter-kit/index.md), [nl](https://www.datafirefly.com/nl/documentation/headless-starter-kit/index.md), [pt](https://www.datafirefly.com/pt/documentation/headless-starter-kit/index.md)
- Index: <https://www.datafirefly.com/en/documentation/llms.txt>

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:

- **Products** — `save_post_product`, `woocommerce_update_product`
- **Categories** — create, update, delete on the `product_cat` taxonomy
- **Orders** — status changes (useful to refresh the account page)
- **Pages** — `save_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 enabled** — `is_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()` 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.
