Problem
Massage Lab ran on Shopify. That made a service business look like a webshop: every treatment was a product with a cart, while the actual booking already happened in Fresha. The site had only Spanish and English, on a coast where many residents and visitors speak Dutch, German or French. And gift cards lived in Shopify, apart from the way the team actually works.
Solution
I replaced the shop with a static Astro site, served from Cloudflare Workers.
- Five languages (ES, EN, FR, NL, DE) with translated URLs per language, for example
/de/behandlungen/and/fr/soins/. One central route map generates every page, the hreflang links and the sitemap, so the languages cannot drift apart. - Treatments in three categories (massages, hair removal, facials), each with prices per duration. A “book” button opens Fresha straight on that treatment, or WhatsApp with the treatment and duration already filled in.
- Gift cards composed on the site: an amount or a treatment, names and a message.
- Migration without lost rankings: the 146 URLs from the old Shopify sitemap became 604 exact 301 rules (one per old URL and language variant, no wildcards). A script checks that every rule is a single hop to a page that answers 200.
- Local SEO: 14 landing pages for towns within about 30 km (Jávea, Calpe, Moraira, Benissa and others).
HealthAndBeautyBusinessstructured data on every page with address, phone and opening hours,Servicedata with prices on each treatment,FAQPageon the FAQ. Sitemap, robots.txt and a generated llms.txt so AI search gets the treatments and prices right. Build checks validate every JSON-LD block, the canonicals and the reciprocal hreflang links. - Design: Newsreader for headings, Red Hat Text for body copy and the Distant Stroke script in the hero, warm near-black on off-white with a moss-green accent.
The team dashboard
The part I am proudest of is not public. In a private dashboard the team (owner and staff) runs the whole gift card flow:
- A request from the website lands in the dashboard with a reference number. The team marks it paid, and the customer gets a personal link to pay, download the card as a PDF and share it on WhatsApp. The recipient gets a separate gift page with the card and the dedication, without price or payment details.
- The PDF is the real printed card (front and ticket side) filled in the browser, with a QR code. Staff who scan it land on that card in the dashboard; a customer lands on the booking page in their own language.
- Cards hold a balance. “Redeem” takes off an amount or a treatment in one atomic step, so a card can never be used twice or go below zero. Every change is logged with who, when and the old and new value.
- “To send today” lists who to message: unpaid requests older than 48 hours, cards about to expire and yesterday’s visits for a review request. One tap opens WhatsApp with the text in the customer’s language. Nothing is sent automatically.
- Also: a monthly summary with CSV export, import of the old Shopify gift cards, the latest Google reviews, a notice banner for the website, and user management. The interface is in all five languages.
It runs on the same Worker with Cloudflare D1 as the database and a nightly backup to R2. Passwords are hashed with PBKDF2, logins and orders are rate-limited, and customer links are stored only as hashes.
Outcome
Live on www.massagelab.es since 29 October 2026, in five languages with 255 pages in the sitemap. The old Shopify URLs redirect in one step to the matching page in the same language. At the time of writing the site shows a 4.9 rating from 104 Google reviews. Gift cards are sold, sent and redeemed in the team’s own dashboard.


