---
name: site-building
description: Construisez pour Spaicely un site statique facile à trouver, à partager et à utiliser, avec sitemap.xml, robots.txt, llms.txt, aperçus de liens pour les réseaux sociaux, données structurées et icônes. À utiliser chaque fois que vous construisez ou retravaillez un site hébergé sur Spaicely, y compris pour déménager un site existant (WordPress, Wix, Squarespace) vers Spaicely.
---

# Construire un site que les personnes et les machines trouvent

Spaicely héberge ce que vous envoyez, tel quel. Tout ce dont un bon site a besoin en plus de ses pages, c'est à vous de le faire. Cette liste est ce que nous attendons de chaque site ; après chaque publication, `sites.versions` affiche un rapport dont les `hints` indiquent ce qui manque encore, chacun avec un `fix`.

## Demandez d'abord

- **Le domaine.** Les sitemaps, les liens canoniques et les aperçus de liens ont besoin d'adresses complètes comme `https://bakkerijjansen.nl/over/`. Utilisez le domaine relié au site (`account.whoami`, `domains.status`). Pas encore de domaine ? Demandez lequel ce sera, et utilisez celui-là.
- **Les langues.** Une seule langue, ou plusieurs dans des dossiers (`/`, `/en/`, `/de/`).
- **Les faits.** Nom, activité de l'entreprise, adresse, téléphone, horaires d'ouverture, comptes sur les réseaux sociaux. Ils vont dans les pages et dans les données structurées. N'inventez jamais une adresse, des horaires ou des avis.

## Fichiers à la racine

| Fichier | À quoi il sert |
| --- | --- |
| `index.html` | La page d'accueil. Chaque autre page est un dossier avec son propre `index.html` (`/over/index.html`), lié sous la forme `/over/`. |
| `404.html` | Pour les adresses qui n'existent pas, dans le style et la langue du site, avec un lien vers l'accueil. |
| `robots.txt` | Laisse entrer tout le monde et indique la sitemap. |
| `sitemap.xml` | Chaque page qui doit être trouvée, avec son URL complète. |
| `llms.txt` | Un court résumé du site en Markdown pour les IA, selon https://llmstxt.org. |
| `favicon.ico`, `icon.svg`, `apple-touch-icon.png` | L'icône dans les onglets, les favoris et sur les téléphones (apple-touch-icon fait 180x180). |
| `site.webmanifest` | Nom, couleurs et icônes (192 et 512 px) quand quelqu'un ajoute le site à son écran d'accueil. |
| `og.jpg` | L'image des aperçus de liens, 1200x630, moins de 300 Ko. Une page peut avoir la sienne. |

`robots.txt` :

```
User-agent: *
Allow: /

Sitemap: https://bakkerijjansen.nl/sitemap.xml
```

`sitemap.xml`, avec pour `lastmod` la date à laquelle la page a vraiment changé pour la dernière fois :

```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url><loc>https://bakkerijjansen.nl/</loc><lastmod>2026-10-26</lastmod></url>
  <url><loc>https://bakkerijjansen.nl/over/</loc><lastmod>2026-10-26</lastmod></url>
</urlset>
```

Laissez de côté `404.html` et les pages marquées `noindex`. Mettez la sitemap à jour chaque fois que vous ajoutez ou retirez une page.

`llms.txt` :

```markdown
# Bakkerij Jansen

> Bakery in Haarlem since 1952: bread, pastry and cakes to order. Open Tuesday to Saturday.

- [About us](https://bakkerijjansen.nl/over/): the family and how we bake
- [Cakes to order](https://bakkerijjansen.nl/taarten/): prices and how to order
- [Contact](https://bakkerijjansen.nl/contact/): address, opening hours, phone
```

## Le head de chaque page

```html
<!doctype html>
<html lang="nl">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Taarten op bestelling | Bakkerij Jansen</title>
  <meta name="description" content="Verjaardag, bruiloft of zomaar: bestel uw taart twee dagen vooraf. Ophalen in Haarlem.">
  <link rel="canonical" href="https://bakkerijjansen.nl/taarten/">

  <meta property="og:type" content="website">
  <meta property="og:site_name" content="Bakkerij Jansen">
  <meta property="og:title" content="Taarten op bestelling">
  <meta property="og:description" content="Bestel uw taart twee dagen vooraf.">
  <meta property="og:url" content="https://bakkerijjansen.nl/taarten/">
  <meta property="og:image" content="https://bakkerijjansen.nl/og.jpg">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta property="og:locale" content="nl_NL">
  <meta name="twitter:card" content="summary_large_image">

  <link rel="icon" href="/favicon.ico" sizes="32x32">
  <link rel="icon" href="/icon.svg" type="image/svg+xml">
  <link rel="apple-touch-icon" href="/apple-touch-icon.png">
  <link rel="manifest" href="/site.webmanifest">
  <meta name="theme-color" content="#7a3e1d">
  <link rel="alternate" type="text/markdown" href="/llms.txt">
</head>
```

- Chaque page a son propre titre (environ 50 à 60 caractères, le sujet d'abord, le nom du site en dernier) et sa propre description (environ 120 à 160 caractères, écrite pour une personne qui se demande si elle va cliquer).
- `canonical`, `og:url`, `og:image` et les entrées de la sitemap utilisent toujours l'adresse `https://` complète sur le domaine du client, avec la même barre oblique finale que les liens.
- Une page qui ne doit pas apparaître dans les résultats de recherche (une page de remerciement après un formulaire) reçoit `<meta name="robots" content="noindex">` et reste hors de la sitemap.

## Données structurées

Décrivez l'entreprise en JSON-LD sur la page d'accueil, pour que les moteurs de recherche puissent afficher l'adresse, les horaires et le logo. Choisissez le type le plus précis sur https://schema.org (`Bakery`, `Restaurant`, `Dentist`, `LocalBusiness`, ou `Organization` sans boutique physique), et ajoutez `WebSite` :

```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Bakery",
      "@id": "https://bakkerijjansen.nl/#business",
      "name": "Bakkerij Jansen",
      "url": "https://bakkerijjansen.nl/",
      "logo": "https://bakkerijjansen.nl/icon-512.png",
      "image": "https://bakkerijjansen.nl/og.jpg",
      "telephone": "+31 23 123 4567",
      "address": { "@type": "PostalAddress", "streetAddress": "Grote Markt 1", "postalCode": "2011 RD", "addressLocality": "Haarlem", "addressCountry": "NL" },
      "openingHoursSpecification": [
        { "@type": "OpeningHoursSpecification", "dayOfWeek": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "opens": "08:00", "closes": "17:00" }
      ],
      "sameAs": ["https://www.instagram.com/bakkerijjansen", "https://www.facebook.com/bakkerijjansen"]
    },
    { "@type": "WebSite", "@id": "https://bakkerijjansen.nl/#website", "url": "https://bakkerijjansen.nl/", "name": "Bakkerij Jansen", "inLanguage": "nl" }
  ]
}
</script>
```

Les articles de blog reçoivent `Article` (avec `datePublished` et `author`), les produits `Product` avec `offers`, les événements `Event`. Ne balisez que ce que le visiteur peut aussi lire sur la page.

## Redirections

Si le site vient d'une autre plateforme, ou si une adresse change, redirigez les anciennes adresses dans un fichier `_redirects` à la racine. Une ligne par redirection :

```
# ancien           nouveau                            statut (301 par défaut)
/a-propos.php      /a-propos/
/blog/*            /actualites/
/boutique          https://boutique.example.fr/       302
```

- Un chemin qui se termine par `/*` redirige tout ce qui est en dessous vers la même cible.
- La cible est un chemin du même site ou une adresse https complète.
- 301 pour une redirection permanente (les moteurs de recherche reprennent alors la nouvelle adresse), 302 ou 307 pour une redirection temporaire.
- 50 lignes au plus ; la redirection passe avant un fichier au même chemin.
- Le fichier lui-même n'est pas publié. Une ligne incorrecte apparaît avec son numéro dans les conseils de la version.

## Plus d'une langue

- Un dossier par langue, chaque page avec `lang` renseigné et la même structure.
- Chaque version d'une page liste toutes les versions, elle-même comprise, plus `x-default` :

```html
<link rel="alternate" hreflang="nl" href="https://bakkerijjansen.nl/taarten/">
<link rel="alternate" hreflang="en" href="https://bakkerijjansen.nl/en/cakes/">
<link rel="alternate" hreflang="x-default" href="https://bakkerijjansen.nl/taarten/">
```

- Un sélecteur de langue visible avec des liens, jamais de redirection automatique selon la langue du navigateur.
- `og:locale` par langue ; la sitemap liste chaque version.

## Rapide, lisible, utilisable

- HTML sémantique : `header`, `nav`, `main`, `footer`, un seul `h1` par page, des titres dans l'ordre.
- Chaque `img` a un `alt` (vide, `alt=""`, pour la décoration) ainsi que `width` et `height`. Les images sous le premier écran reçoivent `loading="lazy"`. Utilisez WebP ou AVIF et dimensionnez les photos à la taille où elles s'affichent, au plus le double pour les écrans nets.
- Peu de JavaScript ; le site doit fonctionner sans. Pas de framework pour un site de quelques pages.
- Les polices sur le site lui-même (`.woff2`, `font-display: swap`), pas depuis les serveurs de Google Fonts : les charger depuis Google envoie l'adresse IP du visiteur à Google, ce pour quoi un tribunal allemand a accordé des dommages et intérêts en 2022.
- Un contraste suffisant, un focus visible, des liens qui disent où ils mènent, un numéro de téléphone en lien `tel:`, une adresse avec un lien vers une carte.
- Spaicely minifie déjà le HTML, le CSS et le JavaScript et recompresse les JPEG et PNG à la publication. Il ne convertit pas les images dans d'autres formats.
- Les navigateurs gardent le CSS, le JavaScript, les images et les polices pendant un jour. Quand vous modifiez un tel fichier, donnez-lui un nouveau nom (`style.v2.css`) et mettez les liens à jour, pour que les visiteurs voient le changement tout de suite. Le HTML est toujours rechargé.

## Formulaires

Un site statique peut quand même recevoir des messages et des inscriptions. Spaicely les traite sur `https://scripts.spaicely.com` ; n'utilisez pas de service de formulaires ailleurs.

1. **Choisissez le type.** `contact` envoie chaque message par mail à une adresse ; `signup` tient une liste (newsletter, liste d'attente) avec double opt-in : le visiteur reçoit un mail et n'est sur la liste qu'après avoir cliqué sur le lien qu'il contient.
2. **Activez-le** avec `scripts.enable` : `site_id`, `kind`, `name` (le nom que la personne lui donne), et selon le cas `locale` (langue des mails et de nos pages), `subject`, `recipient`, `success_url` et, pour signup, `confirmed_url`. Sans `recipient`, un formulaire de contact écrit à la personne qui l'a créé. Toute autre adresse reçoit d'abord un mail de confirmation ; d'ici là, le formulaire a le statut `waiting_for_recipient` et n'envoie rien, dites donc à la personne de regarder dans cette boîte.
3. **Placez le `html` de la réponse tel quel dans une page**, puis mettez-le en forme avec le CSS du site. Il ressemble à ceci :

```html
<form action="https://scripts.spaicely.com/f/k3v9q2x7m1c8b4n6r0td" method="post">
  <input type="text" name="_hp" tabindex="-1" autocomplete="off" aria-hidden="true" style="position:absolute;left:-9999px">
  <label>Naam <input name="name" required></label>
  <label>E-mailadres <input type="email" name="email" required></label>
  <label>Bericht <textarea name="message" rows="6" required></textarea></label>
  <button type="submit">Versturen</button>
</form>
```

   Gardez `action`, `method` et le champ caché `_hp` (il attrape les robots). Vous pouvez ajouter des champs, comme `telephone` ou `objet`, avec n'importe quel nom qui ne commence pas par `_` ; tous arrivent dans le mail. Le champ `email` devient l'adresse de réponse : répondre écrit directement au visiteur. Gardez un `label` visible pour chaque champ.
4. **Créez les pages de remerciement.** `success_url` (après l'envoi) et `confirmed_url` (après la confirmation d'une inscription) sont des pages du site lui-même, des adresses `https://` complètes sur son propre domaine, avec `noindex` et hors du sitemap. Sans elles, le visiteur voit une page simple de chez nous dans la langue du formulaire, avec un lien de retour.
5. **Ou restez sur la page.** Envoyez avec `fetch` et `Accept: application/json` ; la réponse est `{"ok": true}`, ou `{"ok": false, "error": ...}` avec `origin_not_allowed`, `form_inactive`, `invalid`, `rate_limited` ou `failed`. Sans JavaScript, le formulaire normal fonctionne toujours :

```html
<script>
  for (const form of document.querySelectorAll('form[action^="https://scripts.spaicely.com/"]')) {
    const status = document.createElement("p");
    status.className = "form-status";
    status.setAttribute("role", "status");
    form.append(status);
    form.addEventListener("submit", async (event) => {
      event.preventDefault();
      const response = await fetch(form.action, {
        method: "POST", body: new URLSearchParams(new FormData(form)), headers: { Accept: "application/json" }
      }).catch(() => null);
      const answer = response ? await response.json().catch(() => ({})) : {};
      status.textContent = answer.ok ? "Bedankt, uw bericht is verstuurd." : "Dat lukte niet. Probeer het later nog eens.";
      if (answer.ok) form.reset();
    });
  }
</script>
```

**Un téléchargement contre des coordonnées** (un guide, un livre blanc ou une liste de prix) : utilisez `kind: "download"`.

- Placez le fichier dans le site sous `_private/`, par exemple `_private/guide.pdf`, et publiez-le avec le reste. Tout ce qui se trouve sous `_private/` reste hors du CDN et ne peut pas être récupéré depuis le site ; seul le formulaire le donne.
- `scripts.enable` avec `kind: "download"`, `file_path: "_private/guide.pdf"` et éventuellement `recipient`. Ce que le visiteur remplit est envoyé par e-mail à ce destinataire, comme pour `contact`. Le HTML contient nom, e-mail et entreprise ; ajoutez des champs si la personne veut en savoir plus.
- `delivery: "mail"` (par défaut) envoie le lien de téléchargement à l'adresse saisie, ce qui garantit des coordonnées réelles ; indiquez sur la page de remerciement que le lien est dans l'e-mail. `delivery: "direct"` lance le téléchargement tout de suite ; avec `fetch` vous recevez `{"ok": true, "download_url": ...}`, puis faites `location.href = answer.download_url`.
- Un lien fonctionne 7 jours (direct : une heure) et 5 fois au maximum. Tant que le fichier n'est pas dans la version en ligne, le statut est `waiting_for_file` et le formulaire renvoie une erreur.

**Un code de réduction contre une adresse e-mail** : utilisez `kind: "coupon"`.

- Un code pour tout le monde : `coupon_code: "BIENVENUE10"`. Un code unique par visiteur : `coupon_codes: ["JANSEN-001", ...]` (créez-les d'abord dans la boutique ou la caisse ; jusqu'à 1000 par appel, ajoutez-en plus tard avec `scripts.configure`, `clear_coupons: true` retire les codes pas encore donnés).
- Le destinataire reçoit par e-mail ce que le visiteur a rempli et le code qu'il a reçu, ainsi qu'un avis quand il reste 10 codes et quand ils sont épuisés. Sans codes, le statut est `waiting_for_codes`.
- `delivery: "mail"` (par défaut) envoie le code à l'adresse saisie. `delivery: "direct"` ne fonctionne qu'avec des codes que tout le monde reçoit ; avec `fetch` vous recevez `{"ok": true, "code": ...}` à afficher sur la page.
- Codes avec une période : `dated_codes: [{"code": "WEEK45", "valid_from": "2026-11-03", "valid_until": "2026-11-09", "shared": true}]`. Avec `shared: true`, tous ceux qui demandent pendant cette période reçoivent le même code (un code par semaine ou par action) ; sans cela, chaque code n'est donné qu'une fois et l'e-mail indique jusqu'à quand il est valable. Les dates sans heure sont des journées entières à l'heure néerlandaise. Ordre : d'abord un code partagé valable maintenant, puis les codes uniques, puis `coupon_code`.

**Payer pour un téléchargement ou un code** : donnez à un formulaire de téléchargement ou de réduction un `price_cents` (TVA comprise, au moins 50). Le visiteur paie via Stripe au propriétaire du site, comme dans la boutique : formule Boutique, un compte Stripe connecté et 2 % de commission. Après paiement, le lien ou le code part par e-mail à l'acheteur et les coordonnées au destinataire.

- Le formulaire envoie alors vers `https://app.spaicely.com/forms/<form_id>/checkout` ; utilisez le nouveau `html` de la réponse. Un formulaire simple continue vers Stripe ; avec `fetch` (JSON) vous recevez `{"url": ...}` à ouvrir.
- Gardez la case obligatoire `start_now` : l'acheteur le veut tout de suite et renonce ainsi au droit de rétractation pour un contenu numérique. Sans elle, le paiement est refusé.
- Tant qu'il manque quelque chose, le statut le dit : `waiting_for_plan`, `waiting_for_billing_details` ou `waiting_for_stripe` (donnez alors `connect_url` à la personne). `price_cents: 0` rend le formulaire de nouveau gratuit.
- Le propriétaire est le vendeur. Mettez sur le site les coordonnées de l'entreprise, le prix TTC et une page sur la livraison et la rétractation, comme pour la boutique.

**Dons** : `kind: "donation"` fonctionne avec chaque abonnement. Le visiteur choisit un bouton de montant ou saisit un montant et paie via Stripe sur le propre compte du propriétaire (connecté une fois, 2 % de commission). Le donateur reçoit un mail de remerciement, le destinataire un mail par don.

- `donation_amounts` en centimes pour les boutons (par défaut 10, 25 et 50 euros), `donation_min_cents` et `donation_max_cents` pour les limites, `goal_cents` pour un objectif et `ends_at` pour le dernier jour (ensuite le formulaire n'accepte plus rien). Il n'y a pas de case de rétractation : rien n'est livré.
- Le formulaire envoie `amount` (un bouton) ou `amount_other` (saisi), `name`, `email`, `message` et `show_name`. Le nom et le message n'apparaissent dans la progression qu'avec `show_name`.
- Pour une barre de progression : `fetch(progress_url)` donne `{"raised_cents", "donors", "goal_cents", "ends_at", "open", "donations": [{"name", "message", "at"}]}`, ouvert à toute origine et à jour en moins d'une minute. Affichez la barre en pourcentage de `goal_cents` et indiquez clairement sur la page à quoi sert l'argent et qu'il va au propriétaire même si l'objectif n'est pas atteint.
- Pas pour l'investissement, le prêt ou des contreparties en argent ; cela demande un agrément. Offrir quelque chose en retour (une mention, une carte) est permis.

Bon à savoir :

- Un formulaire n'accepte que les envois depuis le domaine connecté du site (et son www). Sur un aperçu ou une copie locale, il répond `origin_not_allowed` ; testez-le une fois le domaine en ligne.
- Limites : 10 envois par visiteur toutes les 10 minutes, 100 par formulaire par heure et 3 inscriptions par adresse par heure.
- Les mails partent de `no-reply@spaicely.com` avec le nom du site comme expéditeur.
- `scripts.list` montre les formulaires d'un site avec leur statut et de nouveau le HTML ; `scripts.configure` change les textes, le destinataire ou les pages, ou désactive un formulaire avec `enabled: false`.
- `scripts.subscribers` donne les inscriptions confirmées, chacune avec une `unsubscribe_url`. Chaque mail envoyé à la liste doit contenir ce lien.
- Mentionnez le formulaire dans la page de confidentialité : ce qui est collecté, que Spaicely le traite et combien de temps c'est conservé.
- Les sessions et un panier n'ont pas besoin de serveur : gardez-les dans le navigateur avec `localStorage`.

## Boutique en ligne

Avec la formule Boutique, un site vend lui-même. L'acheteur paie via Stripe Checkout, directement au propriétaire ; Spaicely retient 2 % de commission. Aucun logiciel de boutique n'est nécessaire : les produits tiennent dans un fichier.

1. **Relier Stripe.** Le propriétaire du compte relie une fois son propre compte Stripe. `shop.setup` donne pour cela `connect_url` ; envoyez la personne vers cette page. D'ici là, le statut est `waiting_for_stripe`.
2. **Les produits dans `data/products.json`.** Prix en centimes, TVA comprise. `id` est unique et ne change pas ; `active: false` retire un produit de la vente sans le supprimer. Sans `shipping`, Checkout ne demande pas d'adresse (services, produits numériques).

```json
{
  "products": [
    { "id": "mok-blauw", "name": "Tasse bleue", "price": 1495, "image": "/img/mok-blauw.jpg", "description": "Tournée à la main, 300 ml" },
    { "id": "bord", "name": "Assiette", "price": 2000 }
  ],
  "shipping": [
    { "id": "nl", "name": "Livraison aux Pays-Bas et en Belgique", "price": 495, "countries": ["NL", "BE"] }
  ]
}
```

3. **Publiez et activez** avec `shop.setup` (`site_id`, et si besoin `success_url`, `cancel_url` sur le domaine du site, `notify_email`). La réponse donne le statut, ce qui manque encore (`problems`), l'`endpoint` et un `snippet` : un bouton d'achat sous forme de simple formulaire.
4. **Un panier** se garde dans le navigateur (`localStorage`) et s'envoie avec `fetch` ; la réponse est `{"url": ...}` pour aller vers Stripe, ou `{"error": ..., "message": ...}` avec un texte pour le visiteur :

```html
<script>
  async function checkout(items, shipping) {
    const response = await fetch("https://app.spaicely.com/shop/SHOP_ID/checkout", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ items, shipping })
    });
    const answer = await response.json();
    if (answer.url) location.href = answer.url; else alert(answer.message);
  }
  // checkout([{ id: "mok-blauw", quantity: 2 }], "nl")
</script>
```

Bon à savoir :

- Les prix viennent toujours du `data/products.json` en ligne, jamais de la page. Si vous changez un prix, publiez aussi les pages qui l'affichent.
- La commande ne fonctionne que depuis le domaine relié du site (et www) ou son adresse ; jusqu'à 50 lignes, de 1 à 99 exemplaires par produit.
- Le propriétaire reçoit un mail pour chaque commande payée, avec les produits et l'adresse de livraison ; `shop.orders` liste les commandes. Stripe envoie le reçu à l'acheteur.
- Créez une page de remerciement (`success_url`, `noindex`) et indiquez dans la page de confidentialité que Stripe traite le paiement.
- Le propriétaire est le vendeur. Créez avec la personne une page sur la livraison, les frais d'envoi, les retours et le droit de rétractation de 14 jours des consommateurs (avec le formulaire type), ainsi que les mentions légales (nom, adresse, numéros d'immatriculation et de TVA), et liez-la dans le pied de page et au paiement. Le propriétaire rembourse dans son tableau de bord Stripe ; la commission de Spaicely n'est alors pas restituée.

## Déménager un site existant (WordPress et autres)

Une personne qui a un site ailleurs peut vous le faire reconstruire ici : les mêmes pages et adresses, en HTML simple et rapide, sans extensions ni mises à jour. Ne déménagez qu’un site qui appartient à la personne ou qu’elle a le droit de déménager.

1. **Faites l’inventaire.** Lisez le plan du site (`/wp-sitemap.xml`, `/sitemap_index.xml` ou `/sitemap.xml`). Sous WordPress, l’API REST donne en général tout : `/wp-json/wp/v2/pages?per_page=100`, `/posts`, `/media` et `/categories` (pages suivantes avec `&page=2`). Pour chaque page vous obtenez `link`, `title`, `content.rendered` et, avec Yoast ou Rank Math, `yoast_head_json` avec le titre et la description pour les moteurs de recherche. Si l’API est désactivée, lisez les pages elles-mêmes, ou demandez un export à la personne (WordPress : Outils > Exporter, un fichier XML). Dressez la liste de toutes les adresses et montrez-la avant de construire.
2. **Reconstruisez, ne copiez pas.** Reprenez pour chaque page le texte, les titres, les images et les liens, et écrivez un HTML propre dans un thème dans le style de l’ancien site (ou dans un nouveau style, si la personne le souhaite). Laissez de côté les shortcodes, le balisage des constructeurs de pages, les scripts d’extensions et le suivi. Gardez le titre et la description de chaque page.
3. **Images.** Passez les fichiers de `wp-content/uploads/` en `source_url` dans `sites.deploy` (20 au plus par appel) ; Spaicely les récupère et les allège. Prenez l’original, pas une miniature, et gardez les textes alternatifs.
4. **Gardez les adresses.** `/a-propos/` devient `a-propos/index.html`, pour que liens et résultats de recherche continuent de fonctionner. Ce qui change malgré tout va dans `_redirects` avec 301 (voir Redirections). Les adresses WordPress qui n’existent plus (`/category/*`, `/tag/*`, `/author/*`, `/feed/`, `/?p=123`) mènent à la page la plus proche.
5. **Ce que faisait une extension.** Les formulaires de contact (Contact Form 7, WPForms, Gravity Forms) deviennent un formulaire de contact, une lettre d’information devient un formulaire d’inscription (voir Formulaires). WooCommerce devient une boutique avec la formule Boutique (voir Boutique en ligne). Un blog devient une page par article plus une page d’aperçu. Les commentaires, la recherche et les connexions ne suivent pas : dites-le, et reprenez les commentaires existants en texte si la personne le souhaite.
6. **Montrez avant de basculer.** Publiez et laissez la personne vérifier le site à sa propre adresse (`https://<label>.spaicely.site`, dès que l’hébergement est payé). Passez-le en revue avec le prompt `launch_check`.
7. **Ensuite le domaine.** `sites.domains.connect` lit les enregistrements DNS actuels du domaine et reprend ceux que la personne garde, surtout le courrier (MX, SPF, DKIM), pour que le courrier continue de fonctionner. Ce n’est qu’une fois le site en ligne sur son propre domaine que l’ancien hébergement peut être résilié. Le domaine lui-même peut aussi passer chez Spaicely avec `domains.transfer`, mais ce n’est pas obligatoire.

## Vie privée et cookies

- Pas de suivi sans consentement. Les statistiques, ou les vidéos, cartes et publications de réseaux sociaux intégrées qui déposent des cookies, demandent un bandeau cookies qui pose d'abord la question, où refuser est aussi simple qu'accepter. Mieux : un outil de statistiques respectueux de la vie privée sans cookies, une vignette vidéo qui ne charge le lecteur qu'au clic, et un lien vers la carte au lieu d'une intégration.
- Une page de confidentialité dès que le site collecte quoi que ce soit : quoi, pourquoi, combien de temps, à qui s'adresser. Les sites d'entreprise dans l'UE affichent aussi la raison sociale, l'adresse, le numéro d'immatriculation au registre du commerce et le numéro de TVA (en Allemagne et en Autriche sur une page `Impressum`).

## Une fois en ligne

- Chaque site payé a aussi sa propre adresse, `https://<label>.spaicely.site` (`address` dans `account.whoami` et `sites.versions` ; `address_live` indique si elle fonctionne). Elle fonctionne dès que l'hébergement est payé et qu'une version est en ligne, même sans domaine relié : pratique pour montrer le site avant que le domaine soit prêt. Les moteurs de recherche y reçoivent noindex, donc les liens canoniques et le sitemap continuent de pointer vers le domaine du site.
- Lisez les `hints` dans `sites.versions` et corrigez ce qui s'applique.
- Proposez à la personne d'ajouter le domaine à Google Search Console et à Bing Webmaster Tools et d'y soumettre la sitemap. Les deux vérifient un domaine avec un enregistrement TXT, que vous pouvez créer avec `domains.dns.update`.
- Après un gros changement, mettez à jour `lastmod` dans la sitemap pour les pages modifiées.

## Pour aller plus loin

- Envoyer, publier et revenir en arrière, modifier un bloc d'une page, mettre le site sur son domaine, le DNS, le transfert de mail, les personnes et les rôles, la formule d'hébergement : la skill `site-management`, https://spaicely.com/skills/fr/site-management/SKILL.md.
- Chaque outil, paramètre et endpoint HTTP : https://spaicely.com/developers/, et pour les IA https://spaicely.com/llms-full.txt.
- Si un outil cité là n'est pas dans votre liste, votre liste est ancienne : demandez à la personne de reconnecter le connecteur Spaicely, qui charge la liste actuelle.
