Cloudflare Workers fournit un runtime rapide et distribué globalement pour EmDash. Ce guide couvre le déploiement avec D1 pour la base de données et R2 pour le stockage des médias.
Prérequis
- Un compte Cloudflare
- Wrangler CLI installé (
npm install -g wrangler) - Authentifié auprès de Cloudflare (
wrangler login)
Configurer les bindings
Créez wrangler.jsonc à la racine de votre projet avec les bindings D1 et R2. Wrangler provisionne les deux ressources lors du premier déploiement si elles n’existent pas encore.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-emdash-site",
"compatibility_date": "2025-01-15",
"compatibility_flags": ["nodejs_compat"],
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db",
},
],
"r2_buckets": [
{
"binding": "MEDIA",
"bucket_name": "emdash-media",
},
],
}
Configurer EmDash
Mettez à jour votre configuration Astro pour utiliser D1 et R2 :
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import react from "@astrojs/react";
import emdash from "emdash/astro";
import { d1, r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
output: "server",
adapter: cloudflare(),
integrations: [
react(), // Requis — l'interface admin est une app React
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
}),
],
});
Premier démarrage
Les migrations de base de données s’exécutent automatiquement lors de la première requête après le déploiement, et à chaque démarrage ultérieur s’il y a de nouvelles migrations à appliquer.
Si la base de données est vide (pas de collections) et que l’assistant de configuration n’a pas été complété, EmDash applique également un fichier seed au premier démarrage. Le seed est lu au moment du build depuis .emdash/seed.json, le chemin dans package.json#emdash.seed ou seed/seed.json — selon ce qui est trouvé en premier — et intégré dans le bundle. Si aucun n’est présent, un seed par défaut intégré est utilisé. Les déploiements ultérieurs sur une base de données existante ne modifient pas son contenu.
Pour modifier le schéma ou le modèle de contenu d’un site déjà déployé, consultez Faire évoluer un site déployé.
Publication programmée
Sur Cloudflare Workers, la publication programmée, les crons de plugins et les tâches de maintenance s’exécutent à partir d’un Worker Cron Trigger. Les nouveaux templates Cloudflare incluent cette configuration automatiquement. Si vous mettez à jour un projet existant, exportez l’entrée du Worker EmDash depuis @emdash-cms/cloudflare/worker :
export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";
Puis ajoutez un Cron Trigger à wrangler.jsonc :
{
"triggers": {
"crons": ["* * * * *"],
},
}
Déployer
Déployez sur Cloudflare Workers :
wrangler deploy
Votre site est maintenant en ligne à https://my-emdash-site.<your-subdomain>.workers.dev.
Read Replicas
Pour les sites distribués globalement, activez la réplication de lecture D1 pour router les requêtes de lecture vers des réplicas proches au lieu de toujours interroger la base de données primaire. Cela réduit considérablement la latence pour les visiteurs éloignés de la région primaire.
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
storage: r2({ binding: "MEDIA" }),
}),
Vous devez également activer la réplication de lecture sur la base de données D1 elle-même dans le tableau de bord Cloudflare ou via l’API REST.
Consultez Options de base de données — Read Replicas pour les modes de session et le fonctionnement de la cohérence basée sur les bookmarks.
Object Cache
Pour réduire la charge de lecture sur D1, mettez en cache les résultats de requêtes de contenu et de configuration dans Cloudflare KV. Les lectures sont servies depuis KV au lieu d’interroger la base de données à chaque requête :
import { d1, r2, kvCache } from "@emdash-cms/cloudflare";
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
objectCache: kvCache({ binding: "CACHE" }),
}),
Consultez Object Cache pour la configuration KV, les options et le comportement d’invalidation.
Workers Cache
Le Workers Cache de Cloudflare ("cache": { "enabled": true } dans wrangler.jsonc) place un cache edge devant votre Worker : les requêtes correspondantes sont servies sans exécuter votre Worker du tout. Cela fonctionne bien avec EmDash :
- Les réponses admin et API d’EmDash envoient
Cache-Control: private, no-storeet ne sont jamais stockées. - Vos pages publiques contrôlent leur propre mise en cache via les en-têtes
Cache-Controlqu’elles retournent.
Deux choses à savoir avant de l’activer :
- Les réponses sans en-tête
Cache-Controlsont quand même mises en cache. Workers Cache applique la fraîcheur heuristique RFC 9111 — un200sans aucun en-tête est mis en cache pendant 2 heures. Donnez à chaque route personnalisée unCache-Controlexplicite (utilisezprivate, no-storepour tout ce qui dépend de la session). - Les pages en cache sont partagées avec les éditeurs connectés. Le cache s’exécute avant votre Worker, il ne peut donc pas être contourné en fonction des cookies de requête. Un éditeur connecté peut recevoir la variante anonyme en cache d’une page publique — sans la barre d’outils d’édition visuelle — jusqu’à l’expiration de l’entrée. Les réponses rendues par les éditeurs ne sont jamais stockées (elles portent
private, no-store), donc rien ne fuit dans l’autre direction.
Domaine personnalisé
Ajoutez un domaine personnalisé dans le tableau de bord Cloudflare :
- Allez dans Workers & Pages > votre worker
- Cliquez sur Custom Domains > Add Custom Domain
- Entrez votre domaine et suivez les instructions de configuration DNS
Accès public R2
Pour servir les médias directement depuis R2 (recommandé pour les performances) :
- Dans le tableau de bord Cloudflare, allez dans R2 > votre bucket
- Cliquez sur Settings > Public access
- Activez l’accès public et notez l’URL publique
- Mettez à jour votre configuration de stockage :
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxx.r2.dev"
}),
Authentification Cloudflare Access
Si votre organisation utilise Cloudflare Access, vous pouvez l’utiliser comme fournisseur d’authentification au lieu des passkeys, offrant une connexion unique via votre fournisseur d’identité existant. La configuration suivante l’active :
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
"Admins": 50,
"Editors": 40,
},
}),
}),
Consultez le guide d’authentification pour toutes les options de configuration.
Sur Workers, le seul gestionnaire intégré email:deliver est un stub de console de développement, donc
les flux dépendant de l’email — connexion par magic-link, invitations d’équipe et notifications de
commentaires — échouent avec “Email is not configured” en production. Le
plugin cloudflareEmail() envoie de vrais emails via
Cloudflare Email Sending
en utilisant un binding Worker natif send_email, sans clés API externes.
1. Enregistrer un domaine d’envoi
Dans le tableau de bord Cloudflare, allez dans Email et vérifiez le domaine (ou l’adresse) depuis lequel vous envoyez. Email Sending rejette les messages d’expéditeurs non vérifiés.
2. Ajouter le binding
Déclarez un binding send_email dans wrangler.jsonc :
{
"send_email": [{ "name": "EMAIL" }],
}
3. Enregistrer le fournisseur
Ajoutez le plugin à votre intégration emdash() :
import { d1, r2 } from "@emdash-cms/cloudflare";
import { cloudflareEmail } from "@emdash-cms/cloudflare/plugins";
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
plugins: [
cloudflareEmail({
from: { email: "[email protected]", name: "My Site CMS" },
replyTo: "[email protected]", // optionnel
binding: "EMAIL", // optionnel, par défaut "EMAIL"
}),
],
}),
4. Activer et sélectionner
Déployez, puis activez le plugin sous Admin → Extensions et choisissez-le comme fournisseur sous Settings → Email.
Options
| Option | Type | Par défaut | Description |
|---|---|---|---|
from | string | { email, name? } | — (requis) | Adresse de l’expéditeur sur un domaine enregistré pour Email Sending. |
replyTo | string | — | Reply-To optionnel, utile quand from est une adresse de sous-domaine no-reply. |
binding | string | "EMAIL" | Nom du binding send_email dans wrangler.jsonc. |
Variables d’environnement
Recommandé : clé de chiffrement
EMDASH_ENCRYPTION_KEY est la clé pour chiffrer les secrets de plugins au
repos (tokens webhook, clés Turnstile, etc.). La clé est validée au
démarrage ; le chiffrement des secrets de plugins l’utilise une fois activé. Configurez-la à
chaque déploiement pour que les secrets soient protégés sans changement de configuration ultérieur.
La clé est fournie par vous et jamais stockée dans la base de données ; seul le texte chiffré est stocké. La perdre signifie perdre chaque secret chiffré avec elle.
Générez une clé et stockez-la comme secret du Worker avec les commandes suivantes :
npx emdash secrets generate
wrangler secret put EMDASH_ENCRYPTION_KEY
Optionnel : surcharges de valeurs stables
EmDash génère automatiquement le secret HMAC de preview et le sel de hachage d’IP des commentateurs et les persiste dans la base de données à la première utilisation. Les variables d’environnement ci-dessous sont des surcharges pour les cas où vous devez fixer la valeur vous-même — par exemple, quand un Worker de preview dans un processus séparé doit partager le secret avec votre site principal.
| Variable | Objectif |
|---|---|
EMDASH_PREVIEW_SECRET | Surcharge pour le secret HMAC de preview auto-généré. |
EMDASH_IP_SALT | Surcharge pour le sel de hachage d’IP des commentateurs auto-généré. |
EMDASH_AUTH_SECRET | Optionnel. S’il est défini, il est utilisé comme source de sel d’IP (sauf si EMDASH_IP_SALT est également défini, qui a priorité), maintenant les hachages d’IP des commentateurs stables pour les installations qui en dépendent déjà. Laissez-le non défini pour un nouveau déploiement. |
Accédez aux variables d’environnement dans votre configuration en utilisant import.meta.env ou le binding env de Cloudflare.
Pour l’inventaire complet de chaque secret qu’EmDash utilise — y compris les emplacements de stockage, les étapes de rotation et ce qui casse quand une clé est perdue — consultez Secrets et gestion des clés.
Déploiements de preview
Déployez une branche de preview :
wrangler deploy --env preview
Ajoutez une section d’environnement à wrangler.jsonc :
{
"env": {
"preview": {
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db-preview",
},
],
},
},
}
Dépannage
”D1 binding not found”
Vérifiez que le nom du binding dans wrangler.jsonc correspond à votre configuration de base de données :
// Doit correspondre : d1({ binding: "DB" })
"binding": "DB"
“R2 binding not found”
Vérifiez que le bucket R2 est correctement lié :
// Doit correspondre : r2({ binding: "MEDIA" })
"binding": "MEDIA"
Erreurs de migration
Si vous voyez des erreurs de schéma, suivez les logs du Worker (wrangler tail) et reproduisez l’erreur pour capturer le message sous-jacent — puis créez une issue avec cette sortie.