# MIGRATION-01 — Apache → OpenLiteSpeed (OLS)

**Date** : 2026-06-25
**Statut** : ⏳ Phase 1 EN COURS (env local OLS up — parité prod). Aucune action prod.
**Stratégie retenue** : serveur Scaleway **neuf en parallèle** + **bascule de l'IP flexible**
(l'ancien serveur Apache reste intact = rollback en secondes). Repro/validation **en local Docker**
(image officielle `litespeedtech/openlitespeed`, rejouable sur Apple `container`, même OCI).

## Pourquoi OLS (gains attendus)
- **LSCache** : cache de page natif au niveau serveur (plugin *LiteSpeed Cache* gratuit) — remplace
  WP Super Cache, avec purge intelligente, vary par cookie (idéal WPML), ESI. Gain majeur.
- **LSAPI (lsphp)** : exécution PHP plus rapide et plus légère en RAM que PHP-FPM.
- **HTTP/3 + QUIC** natif. Conf via WebAdmin (:7080) ou fichiers.
- OLS = open-source (GPL). Limite vs Enterprise : `.htaccess` lu au *graceful restart* (pas par requête).

## Existant à porter (déjà documenté dans ce repo)
- Apache 2.4.58 mpm_event + PHP-FPM 8.3 (socket), HTTP/2, mod_rewrite/headers/ssl.
- vhost `toulouse.conf` ; logs `/var/log/apache2/toulouse.access.log`.
- `.htaccess` : rewrite WP + redirections `en/fr/wp-login.php` ; `uploads/.htaccess` deny PHP (TLO-HARDENING) ; règles WP Super Cache.
- WP Super Cache (vary cookie `wp-wpml_current_language`) + Redis object cache (redis-cache).
- WPML, DISALLOW_FILE_MODS=true, mu-plugin `tlo-hardening.php`.
- Sécurité : UFW, fail2ban (jails sshd + wordpress-auth lisant l'access log Apache), SSH durci, ServerTokens Prod.
- SSL Certbot (toulouse-les-orgues.org + www), pipeline WebP libvips (OPTIM-06).
- PHP ext requises (lsphp83-*) : redis, intl, gd, mbstring, mysqli, curl, xml, zip, soap, exif, bz2, gettext, sodium, opcache.

## Points de rupture à traiter (les vrais risques)
1. **Cache** : WP Super Cache → **LiteSpeed Cache plugin**. Install bloquée par DISALLOW_FILE_MODS
   → déployer via wp-cli/SSH (ou lever temporairement la constante). Configurer **vary sur cookie WPML**.
2. **`.htaccess`** : OLS le lit mais l'applique au restart. Permaliens + règles LSCache OK via le plugin.
   Re-traduire : redirections `en/fr/wp-login`, et le **deny PHP dans uploads** en *context* OLS.
3. **Redis object cache** : compatible — garder redis-cache, ou utiliser l'object cache de LiteSpeed Cache. À trancher (garder redis-cache = moins de changement).
4. **fail2ban** : logpath change → `/usr/local/lsws/logs/.../access.log` (format à valider, filtre wordpress-auth à réadapter).
5. **UFW** : ouvrir **UDP 443 (QUIC)** ; **restreindre 7080 (WebAdmin)** à ton IP only (jamais public).
6. **SSL/Certbot** : passer du plugin apache au mode **webroot** (docroot) ; renouvellement + hook reload OLS.
7. **ServerTokens/signature** → équivalent OLS *Server Signature = Off*.
8. **upload_max_filesize** (2M actuel) → régler dans lsphp + WP.
9. **WebP** : garder le pipeline libvips (OPTIM-06) ; LSCache image optim = QUIC.cloud (éviter, quota déjà un souci).

## Phases

### Phase 0 — Pré-requis
- Provisionner instance Scaleway neuve (Ubuntu 24.04, même gamme/CPU). Noter son IP temporaire.
- Confirmer que l'IP de prod est bien une **flexible IP** détachable.

### Phase 1 — Repro locale (Docker / Apple container)  ← on commence ICI
- `docker compose` : services `openlitespeed` (+lsphp83), `mariadb:10.11`, `redis`.
- Importer `backup/db-pre-webp-*.sql.gz` + `backup/uploads-pre-webp/` (ou dump frais).
- wp-config (DB, WP_REDIS_*, WP_CACHE), salts. Désactiver WPSC, activer LiteSpeed Cache (via wp-cli).
- **Tests** : permaliens, WPML fr/en, login (+en/fr), pages artistes, formulaires CF7, LSCache HIT/MISS, srcset/webp, Redis connecté.
- Livrable : conf OLS validée (vhost + rewrite + context uploads + LSCache vary WPML) → snapshots.

### Phase 2 — Build serveur OLS
- `repo.litespeed.sh` → `openlitespeed` + `lsphp83` + extensions ci-dessus. MariaDB 10.11 (rejouer `snapshots/mariadb/99-tlo-optim.cnf`) + Redis.
- Rejouer le durcissement : SSH drop-in (`snapshots/security/99-tlo-hardening.sshd.conf`), UFW (+UDP443, 7080 restreint), fail2ban (logpath OLS), mu-plugin, uploads deny PHP (context OLS), ServerSignature Off.

### Phase 3 — Données
- Dump DB + rsync `wp-content` (uploads/themes/plugins/mu-plugins) depuis prod → nouveau serveur. Importer, brancher wp-config.

### Phase 4 — Validation sur le serveur OLS (avant bascule)
- Accès via IP temporaire + `/etc/hosts` (ou sous-domaine staging). SSL : webroot ou certs copiés temporairement.
- Checklist complète : 200 partout, WPML, login, CF7, LSCache HIT, webp, Redis, fail2ban actif, headers sécu.

### Phase 5 — Cutover (bascule IP flexible)
- TTL DNS **inchangé** (l'IP ne bouge pas, elle est juste réattachée) → pas de propagation.
- Mode maintenance + dump **delta** final DB + sync diff uploads.
- **Détacher la flexible IP de l'ancien (Apache) → l'attacher au nouveau (OLS)**.
- Certbot : valider/réémettre sur le nouveau (l'IP suit le serveur). Vérif live.

### Phase 6 — Rollback
- KO ? → **réattacher l'IP flexible à l'ancien serveur Apache** (intact) = retour en secondes.
- Conserver l'ancien serveur ~7-14 j avant suppression.

### Phase 7 — Post-migration
- Tuning LSCache (TTL, vary WPML, exclusions admin/panier), QUIC/HTTP3, monitoring.
- MAJ doc : snapshots conf OLS, fail2ban, `docs/security.md`, `docs/server.md`.

## Décisions ouvertes
- Object cache : garder **redis-cache** (reco) vs object cache LiteSpeed.
- Runtime local : **Docker compose** (reco, portable) — rejouable tel quel sur Apple `container`.
- Versionner la conf OLS dans `snapshots/openlitespeed/`.

## Sécurité / réversibilité
- Aucune action sur la prod avant Phase 5. Ancien serveur jamais détruit avant validation.
- Tout le durcissement actuel est rejoué sur le neuf (rien perdu).

## Journal Phase 1 (env local Docker) — 2026-06-25
**Workspace** : `migration/ols-docker-env` (gitignored) — upstream `litespeedtech/ols-docker-env`.
**Stack UP (parité prod)** :
- OpenLiteSpeed 1.8.5 + **lsphp83** (PHP 8.3.30) — prod = 8.3.6, même minor.
- MariaDB **10.11**.18 (prod 10.11.14) ; Redis (alpine, PONG) ; phpMyAdmin 5.2.3.
- `.env` : PHP_VERSION=lsphp83, TimeZone=Europe/Paris ; compose `mariadb:10.11`.
- Ports exposés : 80, 443 (tcp+**udp QUIC**), 7080 (WebAdmin).
- wp-cli `/usr/bin/wp`, php `/usr/bin/php` (+ `/usr/local/lsws/lsphp83/bin/php`).
**Baseline** : WP core déployé `sites/localhost/html`, wp-config câblé (DB `localhost`/host `mysql`/prefix `wp_`).
Commandes : `bin/database.sh -D localhost` puis `bin/appinstall.sh -A wordpress -D localhost`.

### Runbook import du site TLO (Phase 1b — à faire)
1. DB : importer `backup/db-pre-webp-20260624.sql.gz` dans une base dédiée (prefix `tlo_racine_`).
2. wp-content : copier plugins/themes/mu-plugins ; uploads = sous-ensemble représentatif (test webp/srcset) avant le full 12G.
3. wp-config : DB importée, `table_prefix='tlo_racine_'`, `WP_REDIS_HOST='redis'`, `WP_CACHE`, salts.
4. `wp search-replace 'toulouse-les-orgues.org' 'localhost'` (gère le https + sérialisation).
5. Désactiver WP Super Cache + Imagify ; installer/activer **LiteSpeed Cache** (wp-cli) ; vary sur cookie `wp-wpml_current_language`.
6. Tests : `wp core is-installed`, pages (artistes), WPML fr/en, login, header `X-LiteSpeed-Cache: hit`, deny PHP uploads, Redis connecté.
7. Versionner la conf OLS validée (vhost + rewrite + context uploads + LSCache) dans `snapshots/openlitespeed/`.

### Résultats Phase 1 (site TLO importé sur OLS local) — 2026-06-25
Import : DB `db-pre-webp` (77 tables `tlo_racine_`), code rsync prod (31 plugins + thèmes + mu-plugins),
uploads subset 2024-2026 (4.8G). Core aligné sur **6.9.4** (= prod) pour éviter le prompt "DB update".
Cache basculé WPSC→**LiteSpeed Cache**, Redis object cache connecté. search-replace domaine→localhost.

**Validé** ✅ : homepage 200, `/wp-login.php` 200, permalinks `/%postname%/`, Redis drop-in OK,
LSCache full-page **GET miss→hit** confirmé (`X-LiteSpeed-Cache: hit`).

**Findings (à porter dans la conf du futur serveur OLS)** :
- 🔴 **uploads PHP exec** : OLS n'honore PAS `<FilesMatch> Require all denied` du `.htaccess`
  (il a exécuté un `.php` dans uploads → 200). FIX OLS-compatible = règle **rewrite `[F]`**
  (`snapshots/openlitespeed/uploads-php-deny.htaccess-snippet`) → re-testé **403** OK.
  => Toute règle d'accès Apache (Require/Deny/FilesMatch) doit être retraduite en rewrite OU en *context* OLS.
- ✅ **WPML — RÉSOLU (passe dédiée 2026-06-25)** : PAS une régression OLS. Comportement **identique prod (Apache) et local (OLS)** :
  neg_type=3 (`?lang=en`), langues en+fr (défaut fr), `/en/`→301 (slug inexistant), `?lang=en`→404 sur les DEUX.
  hreflang annonce **uniquement `fr`+`x-default`** (aucun `en`), pas de switcher → anglais non exposé.
  **Cause** : English activé dans WPML mais **0 traduction** (tout le contenu est `fr` : 124 artistes, 125 events, 69 pages…).
  => Migration fidèle à 100 %. WPML non bloquant pour OLS.
  **Insight client** : site **fonctionnellement monolingue FR**. WPML (premium, vecteur CVE-2024-6386, non-MAJ sans licence)
  ne sert qu'une version EN vide → **envisager le retrait de WPML** = supprime le vecteur de faille + dépendance premium.
- 🔵 **DB upgrade prompt** = artefact de version (core appinstall 7.0 vs DB prod 6.9.4). Non bloquant ;
  inexistant dans la vraie migration (on copie le core de prod).
- Artefacts versionnés : `snapshots/openlitespeed/` (htaccess.after, httpd_config.conf, snippet uploads-deny).

**Reste Phase 1 (optionnel)** : passe WPML (fr/en complet), test pages artistes avec images (webp/srcset),
mesure perf comparée. Suffisant pour valider la faisabilité OLS = ✅.
