ecloudserv docs

Pages — sites statiques

Un site recopié sur tous les nodes et servi directement par nginx. Aucun processus, aucune RAM réservée.

Pages héberge un site déjà construit : tu envoies un dossier de fichiers, il est recopié sur chacun de nos nodes, et nginx le sert depuis le disque. Il n'y a aucun programme à faire tourner, donc rien à redémarrer, rien à surveiller, et pas de quota mémoire.

Pages ou conteneur ?#

C'est la première décision, et elle se tranche en une question : ton site a-t-il besoin d'exécuter du code à chaque visite ?

PagesConteneur
PourSite vitrine, documentation, application front (React, Vue…), portfolioAPI, rendu serveur, base de données, bot
Ce qui tourneRien — nginx sert des fichiersTon programme, en permanence
RedondanceUne copie sur chaque node, d'officeUne instance
Démarrage à froidAucunLe temps de démarrer ton programme
SauvegardesInutiles — les mises en ligne font l'historiqueÀ planifier
Coût mémoireNulTon enveloppe RAM

À noter

Un site Next.js ou Nuxt peut être des deux : en export statique il va dans Pages ; s'il a besoin de rendu serveur, de routes d'API ou d'ISR, il lui faut un conteneur. Voir Langages supportés.

Mettre un site en ligne#

1

Fais pointer ton domaine

Un enregistrement A vers l'adresse d'un de nos nodes, affichée à la création du site. Si tu as aussi un AAAA, il doit pointer vers un node lui aussi — sinon le certificat échouera sans que rien ne l'explique (voir plus bas).

2

Crée le site

Tableau de bord → PagesNouveau site. Choisis un gabarit : il règle le repli SPA correctement et met en ligne une page d'accueil tout de suite, pour que le domaine affiche quelque chose au lieu d'une erreur.

3

Envoie ton contenu

Trois façons, détaillées ci-dessous : une archive ZIP, un dépôt Git, ou la commande npx ecloudserv-pages.

4

Attends le certificat

Il est demandé automatiquement à Let's Encrypt dès que le domaine résout. Compte quelques minutes.

Les trois façons de publier#

1. Une archive ZIP

Le plus direct. Compresse le contenu du dossier de sortie, pas le dossier lui-même : il doit y avoir un index.html à la racine de l'archive. C'est l'erreur la plus fréquente, et elle est refusée avec un message explicite plutôt que de produire un site vide.

depuis le dossier de sortie
cd dist zip -r ../site.zip .

Limite : 30 Mo d'archive, 60 Mo décompressés, 5 000 fichiers.

2. Un dépôt Git

Onglet Source du site : colle l'adresse d'un dépôt public (GitHub, GitLab ou Codeberg), la branche, et éventuellement le sous-dossier qui contient le site compilé (dist, public…).

Le tableau de bord te donne alors une URL de crochet à coller dans les réglages Webhooks de ta forge : chaque git push redéploie le site.

Rien n'est compilé chez nous

Nous téléchargeons l'archive de ta branche et servons son contenu tel quel. Lancer le npm run build d'un dépôt quelconque sur notre infrastructure reviendrait à y exécuter du code arbitraire fourni par un client. Donc : soit ton dépôt contient déjà le site construit, soit tu utilises la commande ci-dessous.

Un déploiement Git qui échoue est enregistré dans l'historique avec son message d'erreur. C'est indispensable : quand le déclencheur est un crochet appelé par la forge, personne ne lit la réponse HTTP — sans cette trace, une branche renommée donnerait un site qui cesse silencieusement de se mettre à jour.

3. La commande en ligne

C'est la voie à prendre si ton site demande une compilation : elle compile chez toi et n'envoie que le dossier de sortie.

bash
npm run build npx ecloudserv-pages deploy --dir dist --site <id-du-site>

Poser le jeton sur ta machine

C'est l'étape qu'on saute et qui produit le message ✗ Aucun jeton. La commande ne lit jamais tes cookies de navigateur : elle s'authentifie avec une clé d'API, qu'il faut lui donner.

1

Créer la clé

Tableau de bord → Clés d'APICréer une clé, en choisissant la portée lecture et écriture. Une clé en lecture seule suffit pour list et whoami, mais deploy sera refusé en 403. La valeur en clair n'est affichée qu'une fois : copie-la tout de suite.
2

La donner à la commande

Trois façons, de la plus pratique à la plus jetable — voir les onglets ci-dessous.
3

Vérifier avant de publier

npx ecloudserv-pages whoami répond par ton nom d'utilisateur si le jeton est accepté. C'est la façon la plus rapide de séparer « mauvaise clé » de « mauvais site ».
:: Pour la fenêtre en cours seulement set ECLOUDSERV_TOKEN=ecl_xxxxxxxx npx ecloudserv-pages whoami :: Ou définitivement, pour toutes les fenêtres À VENIR. :: (setx n'affecte pas la fenêtre courante : rouvre-la ensuite.) setx ECLOUDSERV_TOKEN ecl_xxxxxxxx

Le jeton ne va JAMAIS dans un fichier versionné

Ni dans ecloudserv-pages.json, ni dans un .env poussé sur une forge. Une clé d'API donne les mêmes droits que ton compte ; une clé publiée par mégarde se révoque dans le tableau de bord, mais tout ce qu'elle a permis entre-temps est déjà fait. Dans un CI, passe par les secrets de la forge.

L'identifiant du site s'obtient avec npx ecloudserv-pages list, ou se lit dans l'adresse de sa page dans le tableau de bord.

// ecloudserv-pages.json, à la racine du projet — versionnable. // Le jeton, lui, reste dans ECLOUDSERV_TOKEN. { "site": "abc123", "dir": "dist" }

.git, node_modules, .env, .DS_Store et Thumbs.db sont écartés d'office — ils n'ont rien à faire sur un site public. Pour le reste, un .ecloudservignore dans le dossier publié, un motif par ligne (*.map, rapport/) ; ou --ignore directement sur la ligne de commande. Un nom sans / s'applique à tous les niveaux, et * ne traverse pas les dossiers — il n'y a volontairement ni ! ni **.

Les autres commandes

CommandeCe qu'elle fait
deployCompresse le dossier et le met en ligne.
pack --out site.zipConstruit l'archive sans l'envoyer, pour l'ouvrir et vérifier.
listTes sites et leur identifiant.
status --site <id>Certificat, contenu servi, règles retenues.
deployments --site <id>Historique des mises en ligne.
rollback <id> --site <id>Remet en ligne une version précédente.
whoamiVérifie que le jeton est accepté, sans rien publier.
initÉcrit ecloudserv-pages.json.

--dry-run prépare tout et n'envoie rien : c'est la façon la plus rapide de vérifier qu'on pointe le bon dossier. Après un déploiement réussi, les lignes de _redirects et _headers refusées sont affichées — sans ça, une redirection mal écrite est ignorée en silence et on cherche longtemps pourquoi elle ne s'applique pas.

Dans un script d'intégration continue

Les codes de sortie sont distincts pour qu'un script sache quoi faire : 0 publié, 1 erreur d'usage (dossier absent, site non indiqué), 2 refus de l'API (jeton, droits, site inconnu, archive trop grosse), 3 API injoignable. Seul le 3 mérite d'être réessayé — et les coupures réseau, les 429 et les 5xx le sont déjà trois fois, avec une attente croissante : un redémarrage d'API pendant un déploiement ne casse pas la chaîne. Un 4xx n'est jamais réessayé, insister ne changerait rien.

L'archive est déterministe : deux exécutions sur le même dossier produisent le même fichier octet pour octet. Les liens symboliques sont suivis — les dossiers de sortie des monorepos en contiennent, et les ignorer publiait des sites auxquels il manquait des fichiers sans le moindre avertissement.

Gabarits#

Un gabarit ne compile rien : il règle ton site correctement et te donne la commande et le dossier de sortie exacts de ton framework, y compris les options qui ne s'inventent pas.

GabaritCompilationDossier à envoyerRepli SPA
HTML statique.non
Next.jsnext buildoutnon
Nuxtnuxi generate.output/publicnon
Astroastro builddistnon
Vite (React, Vue, Svelte)vite builddistoui
SvelteKitvite buildbuildnon
Documentationvitepress build.vitepress/distnon

Les réglages qui piègent

  • Next.js : il faut output: "export" dans next.config.js, sinon next build ne produit pas de dossier out. Et images.unoptimized: true, l'optimiseur d'images exigeant un serveur.
  • Nuxt : nuxi generate, pas nuxi build — le second produit un serveur Node, que nginx seul ne peut pas servir.
  • SvelteKit : installe @sveltejs/adapter-static, l'adaptateur par défaut produit un serveur.
  • Astro : laisse output: "static" (le défaut) — un adaptateur SSR produirait un serveur.

Le repli SPA, en une phrase

Activé, toute URL inconnue renvoie index.html — ce qu'exige un routeur côté client. À ne surtout pas activer sur un site à plusieurs fichiers HTML : il transformerait chaque vraie 404 en page d'accueil, rendant les liens morts invisibles et faisant indexer des doublons aux moteurs de recherche.

Redirections et en-têtes#

Deux fichiers texte à la racine de ton site, lus à chaque mise en ligne et traduits en configuration serveur. C'est la convention de Netlify et de Cloudflare Pages : si tu l'as déjà utilisée ailleurs, tes fichiers marchent tels quels.

_redirects

_redirects
# source destination code (301 par défaut) /ancien-article /blog/nouveau 301 /blog/* /articles/* 301 /promo https://boutique.fr/x 302 /app/* /index.html 200
  • un * final capture la suite du chemin et la recolle à la destination : /blog/mon-article devient /articles/mon-article ;
  • codes acceptés : 301, 302, 303, 307, 308, et 200 ;
  • 200 est une réécriture interne : le visiteur garde son URL et on lui sert un autre fichier. C'est le repli d'une application monopage, en plus précis que l'option « Monopage » qui l'applique partout.

_headers

_headers
/* X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin /assets/* Cache-Control: public, max-age=31536000, immutable

Un chemin en début de ligne, ses en-têtes indentés en dessous. Les en-têtes que le serveur calcule lui-même (Content-Length, Transfer-Encoding, Date, Server…) sont refusés : les imposer donnerait des réponses incohérentes selon le navigateur.

Une ligne refusée est signalée, pas devinée

Ces règles finissent dans la configuration du serveur : le filtrage est volontairement strict, et une ligne mal écrite est rejetée plutôt que nettoyée. Les lignes refusées apparaissent dans l'aperçu du site, avec leur numéro et la raison — sans ça, une redirection « pourtant écrite » ne s'applique pas et rien ne dit pourquoi.

Ces fichiers vivent dans ton dépôt, à côté de ton code. Ils ne sont pas modifiables depuis le tableau de bord : il y aurait alors deux sources de vérité, et la prochaine mise en ligne écraserait silencieusement ce que tu aurais réglé à l'écran.

Mises en ligne et retour arrière#

Chaque publication crée une mise en ligne immuable, avec sa date, sa source (envoi / Git / gabarit) et sa référence. L'onglet Mises en ligne permet de rebasculer sur une précédente : c'est instantané, rien n'est reconstruit — l'archive existe déjà.

Attention

Seules les cinq dernières restent restaurables. Au-delà, la trace demeure dans l'historique mais l'archive est libérée : c'est elle qui pèse, et une version d'il y a vingt publications ne sera pas restaurée.

Répartition et cache#

Le tableau de bord ne pousse pas de fichiers vers les nodes : il publie un état(« ce node doit avoir ces sites, dans ces versions ») et chaque node va chercher ce qui lui manque. C'est ce qui rend l'ensemble réparable tout seul — un node qui rejoint la plateforme, ou qui a perdu son disque, se resynchronise au cycle suivant sans qu'on rejoue le moindre déploiement.

Le basculement d'une version à l'autre est atomique : aucun visiteur ne peut tomber sur un site à moitié extrait.

Cache navigateur

FichiersCache-ControlPourquoi
Empreintés (app.a1b2c3d4.js, _next/static/)max-age=31536000, immutableLeur nom change quand leur contenu change : le navigateur ne redemande jamais.
Autres ressources (images, CSS, polices)max-age=3600Une heure, puis revalidation.
HTMLmax-age=0, must-revalidateIl change à chaque mise en ligne à la même URL : le mettre en cache long servirait l'ancienne page pendant des heures après un déploiement.

Sécurité#

Un site statique n'exécute rien, il n'a donc pas de faille applicative — mais il peut toujours être noyé sous le trafic. L'onglet Sécurité expose le même bouclier L7 que les domaines proxifiés :

  • Limites strictes — bride plus tôt les visiteurs anormaux ;
  • Mode attaque — vérifie chaque nouveau visiteur avant de servir la page ;
  • Protection anti-bots et blocage des robots d'IA ;
  • Vérification en cas de dépassement — au lieu d'un 429 sec, le visiteur passe une vérification et repart. Activé par défaut.

La limitation de débit s'applique aux pages uniquement, jamais aux ressources : un seul chargement en tire trente d'un coup, et les compter reviendrait à bloquer quelqu'un pour avoir simplement ouvert le site.

La liste noire d'adresses de ton compte s'applique aussi à tes Pages, en HTTP comme en HTTPS. Voir Sécurité et Pare-feu applicatif.

Référencement#

L'onglet Référencement génère et sert robots.txt et sitemap.xml. Ils sont posés par-dessus le contenu déployé, à chaque cycle : corriger une ligne de robots.txt ne demande donc pas de redéployer tout le site.

À noter

Retirer le fichier et le servir vide sont deux choses différentes : un robots.txt vide dit « aucune règle », son absence dit « je n'en ai pas ». Le bouton Retirer l'efface vraiment des nodes — sans quoi un fichier supprimé resterait servi indéfiniment.

Le certificat n'arrive pas#

L'onglet Aperçu pose un diagnostic complet plutôt qu'un message générique. Dans l'ordre des causes réelles :

Le DNS ne pointe pas (encore) vers un node

Let's Encrypt valide par le domaine : tant qu'il ne résout pas vers une de nos adresses, rien ne peut aboutir. Le diagnostic affiche ce que le domaine résout et les adresses acceptables.

Un enregistrement AAAA égaré

Attention

C'est la cause silencieuse. Let's Encrypt privilégie l'IPv6 quand elle existe : si ton AAAA mène ailleurs (ou nulle part), la validation échoue alors que « le DNS est correct ». Un contrôle qui ne regarde que l'IPv4 déclare tout bon. Le nôtre le signale.

Le site n'a pas encore de contenu

Un site sans mise en ligne n'a pas de dossier sur les nodes : il n'est pas ajouté à la configuration nginx, donc le domaine tombe sur la page d'accueil d'nginx. Le diagnostic sait le distinguer d'une vraie panne — il vérifie que ton domaine figure dans la configuration réellement envoyée aux nodes, et sonde le chemin de validation de bout en bout.

Limites#

Limite
Archive envoyée30 Mo
Contenu décompressé60 Mo
Un seul fichier25 Mo
Nombre de fichiers5 000
Dépôt Git téléchargé80 Mo
Mises en ligne restaurables5
robots.txt / sitemap.xml16 Ko / 512 Ko

Les fichiers volumineux (vidéos, jeux de données, téléchargements) ont leur place dans le stockage objet, pas dans l'archive du site : celle-ci est recopiée sur chaque node.

Ce que Pages ne fait pas#

  • Aucune compilation côté serveur. Voir plus haut : c'est un choix de sécurité, pas un manque.
  • Aucun dépôt privé. Il n'y a ni application OAuth, ni jeton d'accès à protéger. Pour un dépôt privé, utilise npx ecloudserv-pages depuis ton intégration continue.
  • Pas de code côté serveur : ni redirections dynamiques, ni fonctions, ni en-têtes par route. Il te faut un conteneur, ou un domaine en reverse proxy.
  • Pas de certificat générique (*.exemple.fr) : il demande une validation DNS que nous ne pilotons pas.
  • Un domaine ne peut pas être à la fois une Page et un proxy — les deux produiraient le même server_name, et nginx servirait celui que le hasard de génération place en premier. Retire-le des Domaines d'abord.

Référence#

GET/pagesportée read
Tes sites, avec leur mise en ligne servie.
POST/pagesportée write
{ name, domain, spa?, ssl?, template? }
GET/pages/templatesportée read
Le catalogue des gabarits, avec commandes et dossiers de sortie.
GET/pages/:id/dnsportée read
Diagnostic complet : résolution, adresses de nodes, présence dans la configuration poussée, sonde HTTP et test du chemin de validation.
POST/pages/:id/deployportée write
Mise en ligne d'une archive ZIP, en multipart/form-data.
PUT/pages/:id/gitportée write
Branche un dépôt : { repo, branch?, subdir?, autoDeploy? }. repo: null débranche tout et retire le crochet.
POST/pages/:id/deploy-gitportée write
Redéploie depuis le dépôt branché, à la demande.
POST/pages/:id/rollback/:deployIdportée write
Rebascule sur une mise en ligne précédente.
PATCH/pages/:id/shieldportée write
Bouclier L7 du site.
POST/pages/hook/:token
Crochet de déploiement — route publique, appelée par ta forge. Le jeton est la seule authentification et n'autorise qu'une chose : redéployer ce site depuis le dépôt déjà enregistré. Le connaître ne permet donc pas de publier un contenu arbitraire.