From Classic WordPress to a Headless Production Site
Production-ready migration for a local lutherie workshop
Esprit Guitare is a lutherie and electronics repair workshop based in Schœlcher, Martinique. The public website originally ran as a classic WordPress install (OceanWP theme, multiple plugins for booking, opening hours, reviews, forms, and SEO).
In July–August 2026, the site was migrated to a headless architecture: WordPress remains the editorial CMS, while a Next.js application serves the public frontend, APIs, and workshop admin tools. The visual identity stays close to the original site, with custom replacements for the heaviest plugins — especially appointment booking.
Live site: <https://www.espritguitare.fr/>
Context & Goals
- Keep WordPress for content editing (pages, posts, media)
- Remove front-end dependency on heavy WordPress plugins
- Rebuild booking, opening hours, contact/quote forms, and reviews in the Next.js app
- Preserve SEO (canonical URLs, redirects, sitemaps, cutover without downtime)
- Deploy on a self-hosted VPS with Coolify (same pattern as other axialdata projects)
Technical Stack
Frontend:
- Next.js 16 (App Router) + React 19
- TypeScript 5.9
- Tailwind CSS 4
- Local SVG icons (no Font Awesome)
- Zod 4 for validation
- Cloudflare Turnstile on public forms and workshop login
CMS & content:
- Headless WordPress
- WPGraphQL with a custom endpoint slug (not the default `/graphql`)
- HTML rewrite layer for media URLs, shortcodes, and image optimization via `/_next/image` (AVIF/WebP)
- BreadcrumbList JSON-LD on marketing pages
Business data & APIs:
- MongoDB + Mongoose 9 (opening hours, appointments, clients, reviews, blacklist, admin sessions, error logs)
- Custom booking module (30-minute slots, date/slot blocks, blacklist by email / phone / name)
- Twilio SMS + SMTP (Proton) for confirmations, J−1 reminders, review asks, and “instrument ready” notices
- Nextcloud **CalDAV** push (create / update / cancel events) — public ICS subscription feed retired (`410`)
- Nextcloud **CardDAV** sync (upsert client contacts via Mongo `Client` records)
- Client self-service (`/mon-rdv`) + downloadable client ICS
- Reviews: MongoCRUD in workshop admin; homepage carousel (`showOnHome`); `/avis` platform cards + paginated list
Deployment & ops:
- Docker standalone image (`node:22-alpine`) on Coolify
- pnpm via Corepack (`packageManager` in `package.json`)
- GitLab self-hosted (`gitlab.axialdata.app`)
- Rybbits analytics (`analytics.axialdata.app`) without visitor tracking cookies
- Traefik rules: block legacy WP paths on `www` (`/wp-json`, `/wp-admin`, …) and redirect `/wp-content/…` crawls
Architecture
WordPress CMS (Hostinger) Next.js app (Coolify VPS)
cms on espritguitare.fr www + apex espritguitare.fr
• pages / posts / media • public site + API routes
• WPGraphQL • workshop admin
• no public front • staging: preprod (noindex)
│ │
└──────── GraphQL ───────────────────┤
▼
MongoDB · SMTP · Twilio · Nextcloud
(RDV, hours, clients, reviews,
blacklist, CalDAV, CardDAV)
Menus, phone, address, and review outbound links live in the Next.js codebase (siteConfig) — not WordPress menus — to keep the CMS thin.
Security & Hardening
- Strict Content Security Policy with nonces
- Security headers (HSTS, frame denial, referrer policy, etc.)
- Turnstile on contact, quote, booking, and workshop login
- Workshop admin: password auth, HTTP-only session cookie, Mongo-backed rate limit (5 failures / 15 min / IP)
- Signed tokens for client self-service and review opt-out
- Preprod blocked from search indexing (
robots,X-Robots-Tag, empty sitemap when not production) - WordPress front disabled after cutover — CMS only
- Edge filtering of legacy WordPress attack / scrape paths on the public host
Main Features
Public site:
- Marketing pages and blog / réalisations listings from WordPress
- Layout close to the legacy OceanWP design
- Contact and quote forms (file upload on quote) with e-mail delivery
- Search over published content (
/recherche) - Opening hours status on the homepage (Mongo-driven)
- Reviews page (
/avis): leave-a-review CTAs, platform cards with per-source counts, paginated list of all Mongo reviews - Homepage reviews carousel (random mix by platform,
showOnHomeflag) - Article lightbox, TOC, reading time, SEO metadata (Open Graph / Twitter) + breadcrumbs JSON-LD
Booking (replaces Appointment Hour Booking):
- Calendar wizard with available days and time slots
- Blacklist enforcement server-side
- Client + admin e-mail confirmations; SMS confirmation and reminder (~24h before)
- Nextcloud CalDAV sync for the workshop agenda
- Nextcloud CardDAV upsert on booking (via
Clientfiche) - Client ICS download + self-service cancel / reschedule (
/mon-rdv)
Workshop back-office:
- Ops dashboard (today / this week)
- Appointments: create, reschedule, cancel, notification history
- Instrument deposit flag (
skipReviewAskAt— excludes auto review ask) - Manual review solicit + “instrument ready for pickup” mail/SMS
- Clients CRUD (search, merge by phone/email rules, CardDAV)
- Opening hours: weekly schedule + exceptional closures (create / edit / delete)
- Slot blocks, blacklist, reviews CRUD, settings (timezone, slot duration)
- App error log
SEO cutover:
- DNS: apex +
www→ Next.js - Permanent redirects for legacy WordPress URLs
- Trailing-slash convention aligned with historical WordPress URLs
- Sitemap and canonical URLs generated by Next.js
Migration Approach
Work was delivered in phases (all shipped):
- Foundation — Next.js scaffold, WPGraphQL content, design parity, forms, mail, SEO basics, Coolify deploy
- Opening hours — Mongo module + homepage integration
- Booking — full RDV stack (API, UI, SMS, admin, blacklist)
- Reviews — initially WP-backed display; later moved to Mongo (import one-shot, mu-plugin retired)
- Cutover — DNS, headless CMS, redirects, preprod noindex, production hardening (7–8 Aug 2026)
- Phase 7 — CalDAV push, ops week view, error logs, self-service
/mon-rdv, client ICS (13 Aug 2026) - Phase 8 — Mongo
Client+ CardDAV; automated review ask J+1 with opt-out; instrument deposit / ready-pickup flows (Aug–Sep 2026)
The portfolio’s own headless WordPress + Next.js pattern was reused as a baseline, then extended with workshop-specific booking and ops features.
Outcome
- Public site served by Next.js in production since 7–8 August 2026
- WordPress reduced to a content/media CMS
- Booking, hours, clients, and reviews no longer depend on third-party WordPress front plugins
- Staging environment available for safe iteration without SEO duplication
- Ongoing: relocation checklist (timezone, address, Twilio FR, Google Business Profile, etc.)
Resources:
- Production website: https://www.espritguitare.fr/
- Private project (GitLab self-hosted)



