Cloudflare Workers
Acheminez les requêtes /docs vers votre site de documentation Jamdesk via un Cloudflare Worker. Configuration, routes et cache.
Un Cloudflare Worker intercepte les requêtes sur /docs de votre domaine, les réécrit vers votre sous-domaine Jamdesk, puis renvoie la réponse, le tout en périphérie (edge), sans serveur d'origine requis. Vous pouvez générer automatiquement le Worker avec npx jamdesk deploy-proxy cloudflare ou le configurer manuellement ci-dessous.
Fonctionnement
Le Worker transmet les requêtes vers votre sous-domaine Jamdesk et inclut votre domaine dans l'en-tête X-Jamdesk-Forwarded-Host, que Jamdesk utilise pour vérifier le domaine et appliquer ses paramètres. Il s'agit d'une configuration unique — si vous modifiez ultérieurement votre domaine ou votre configuration dans le dashboard, le Worker n'a pas besoin d'être mis à jour.
Prérequis
- Un compte Cloudflare avec votre domaine configuré
- Wrangler CLI v3.0+ installé
- Votre sous-domaine Jamdesk (disponible dans les paramètres du dashboard)
- Votre domaine personnalisé ajouté à votre projet dans le dashboard Jamdesk (le Worker renvoie une erreur 403 tant que le domaine n'est pas enregistré et vérifié)
- Un enregistrement DNS proxifié (nuage orange) sur le nom d'hôte qui sert votre documentation. Les Workers ne s'exécutent que sur des enregistrements proxifiés : un domaine qui n'héberge rien d'autre en a donc quand même besoin. Ajoutez un enregistrement
AAAAfictif pointant vers100::, puis activez le proxy.
Configuration rapide avec la CLI
Le moyen le plus rapide de configurer votre Cloudflare Worker :
npx jamdesk deploy-proxy cloudflare
Cette commande interactive va :
- Vérifier que wrangler 3.0+ est installé
- Vérifier votre compte Cloudflare et afficher les domaines disponibles
- Résoudre votre sous-domaine Jamdesk à partir du projet lié dans
docs.json - Vous permettre de sélectionner votre domaine cible parmi vos zones Cloudflare
- Générer tous les fichiers requis
- Déployer sur Cloudflare (facultatif)
Si vous avez accès à plusieurs comptes Cloudflare (cas fréquent pour les agences ou les équipes), la CLI vous invite à en choisir un avant la sélection de la zone. Choisissez le compte propriétaire du domaine sur lequel vous déployez — les routes Workers ne peuvent être créées que pour des domaines appartenant au compte sélectionné.
Configuration non interactive
Pour une exécution sans invites — en CI ou depuis un script — passez les réponses en options :
jamdesk deploy-proxy cloudflare --slug myproject --domain example.com --yes
--yes génère les fichiers du Worker puis s'arrête. Il ne déploie jamais : la zone est déduite de votre domaine au lieu d'être confirmée auprès de votre compte Cloudflare, la mise en ligne reste donc une étape explicite. Terminez avec :
cd cloudflare-worker
npx wrangler deploy
Avec --yes, la CLI ne peut pas afficher d'invites, elle doit donc connaître votre sous-domaine à l'avance. Elle le lit à partir du projet lié dans docs.json ; si ce lien est absent (exécutez jamdesk deploy une fois pour l'ajouter), passez --slug explicitement, sinon la commande s'arrête plutôt que de deviner.
Si le répertoire de sortie existe déjà, --yes s'arrête au lieu de le remplacer. Ajoutez --force pour l'écraser, ou --output-dir pour générer ailleurs.
| Option | Description |
|---|---|
--slug | Votre sous-domaine Jamdesk, le X dans X.jamdesk.app (ignore la détection automatique) |
--domain | Domaine cible (ex. : yoursite.com) |
--path | Préfixe de chemin ; doit correspondre exactement au sous-chemin défini dans votre tableau de bord (par défaut : /docs) |
--output-dir | Répertoire de sortie (par défaut : cloudflare-worker/) |
--skip-deploy | Ignore l'invite « déployer maintenant ? » lors d'une exécution interactive (--yes ne déploie jamais) |
--force | Écrase le répertoire de sortie s'il existe déjà |
--yes | Répond à chaque invite par sa valeur par défaut (mode CI). Ne déploie jamais et n'écrase jamais un répertoire existant — combinez avec --force pour cela |
Si vous préférez une configuration manuelle, poursuivez avec les étapes ci-dessous.
Configuration manuelle
Étape 1 : créer un Worker
Créez un nouveau répertoire pour votre Worker et initialisez-le :
mkdir docs-proxy && cd docs-proxy
npm init -y
Étape 2 : ajouter le code du Worker
Créez index.js avec le code suivant :
/**
* Jamdesk Documentation Proxy Worker
*
* Generated by: jamdesk deploy-proxy cloudflare
* Proxies /docs/* requests AND their assets to YOUR_SLUG.jamdesk.app
*
* Assets under /_jd/* (images, fonts, branding, analytics) must also be proxied
* since they use absolute paths in the HTML.
*/
const JAMDESK_HOST = "YOUR_SLUG.jamdesk.app";
// Paths that are always proxied to Jamdesk
const PROXY_PATHS = [
"/docs", // Documentation pages
"/_jd/", // All Jamdesk assets (images, fonts, branding, analytics)
];
function shouldProxy(pathname) {
return PROXY_PATHS.some(prefix => {
// For the docs path, require exact match or prefix with slash (not /docs.json)
if (prefix === "/docs") {
return pathname === "/docs" || pathname.startsWith("/docs/");
}
return pathname.startsWith(prefix);
});
}
function proxyToJamdesk(request, url) {
// Rewrite the request to Jamdesk
const proxyUrl = new URL(request.url);
proxyUrl.hostname = JAMDESK_HOST;
// Pin the scheme: an http:// visitor proxied as http:// gets a 308 from
// Vercel's edge pointing at JAMDESK_HOST, and redirect:"manual" hands that
// redirect straight to the browser — bouncing the visitor off this domain.
proxyUrl.protocol = "https:";
// Setting .protocol leaves a non-default .port in place, so :8787 (wrangler
// dev) or Cloudflare's alternate http ports (8080, 8880, 2052…) would follow
// us to the https upstream and fail there.
proxyUrl.port = "";
// Clone headers and add proxy headers
const headers = new Headers(request.headers);
headers.set("Host", JAMDESK_HOST);
headers.set("X-Forwarded-Host", url.hostname);
headers.set("X-Forwarded-Proto", "https");
// Custom header for domain verification (Vercel strips standard forwarding headers)
headers.set("X-Jamdesk-Forwarded-Host", url.hostname);
// Don't follow redirects — let the browser handle them so the URL updates.
// Without this, redirects happen internally and the browser URL doesn't change,
// which causes the sidebar to mis-highlight the active page.
const proxyRequest = new Request(proxyUrl, {
method: request.method,
headers,
body: request.body,
redirect: "manual",
});
// Cache all content types at Cloudflare edge (CF doesn't cache HTML by default).
// Cache duration is controlled by upstream Cache-Control headers.
// Never cache redirects or errors — they must always hit origin.
return fetch(proxyRequest, {
cf: {
cacheEverything: true,
cacheTtlByStatus: { "300-399": 0, "400-499": 0, "500-599": 0 },
},
});
}
export default {
async fetch(request) {
const url = new URL(request.url);
if (shouldProxy(url.pathname)) {
return proxyToJamdesk(request, url);
}
// /_next/ is ambiguous: the customer's root site may itself be a Next.js app
// serving its own /_next/ assets. Try the origin first and fall back to
// Jamdesk when the origin doesn't have it (404) or can't answer at all
// (5xx — a docs-only domain has no real origin, so Cloudflare returns 522).
// Hashed asset filenames never collide between the two apps.
if (url.pathname.startsWith("/_next/")) {
let originResponse;
try {
originResponse = await fetch(request);
} catch (err) {
// Log rather than swallow: a genuine platform fault and a domain with
// no origin at all are indistinguishable in `wrangler tail` otherwise.
console.warn("origin fetch threw, serving from Jamdesk:", err);
return proxyToJamdesk(request, url);
}
if (originResponse.status !== 404 && originResponse.status < 500) {
return originResponse;
}
return proxyToJamdesk(request, url);
}
return fetch(request);
},
};Remplacez YOUR_SLUG par votre sous-domaine Jamdesk réel (par exemple, acme si votre documentation se trouve à acme.jamdesk.app).
L'en-tête X-Jamdesk-Forwarded-Host est obligatoire ; s'il est manquant, l'échec est silencieux — les requêtes réussissent toujours, mais les pages sont servies avec noindex et des liens canoniques pointant vers YOUR_SLUG.jamdesk.app au lieu de votre domaine, si bien que les moteurs de recherche n'indexent jamais votre documentation. Une erreur 403 est le problème inverse : l'en-tête est présent, mais désigne un domaine qui n'est ni enregistré ni actif pour ce projet.
Étape 3 : configurer wrangler.toml
Créez wrangler.toml pour configurer votre Worker :
name = "docs-proxy"
main = "index.js"
compatibility_date = "2024-01-01"
# Serve only via the routes below, not on the public <name>.workers.dev URL —
# that URL is a second way into the same proxy and is worth closing.
workers_dev = false
# Single catch-all route; the worker handles path filtering internally
routes = [
{ pattern = "yoursite.com/*", zone_name = "yoursite.com" },
]Si votre compte Cloudflare vous donne accès à plusieurs comptes, ajoutez également account_id = "<votre identifiant de compte>" — sinon wrangler deploy s'arrête plutôt que de deviner dans quel compte déployer. npx wrangler whoami liste vos identifiants de compte.
Si votre site sert également du trafic sur www.yoursite.com, ajoutez une seconde route pour que le Worker gère les deux :
routes = [
{ pattern = "yoursite.com/*", zone_name = "yoursite.com" },
{ pattern = "www.yoursite.com/*", zone_name = "yoursite.com" },
]Étape 4 : déployer
Déployez votre Worker sur Cloudflare :
npx wrangler deploy
Étape 5 : vérifier
Visitez https://yoursite.com/docs pour vérifier que votre documentation est correctement servie.
Dépannage
Si vous avez renommé votre sous-chemin dans le tableau de bord (par exemple /docs → /help) mais que le tableau PROXY_PATHS de votre Worker ne liste toujours que /docs, les requêtes vers /help/* n'atteignent jamais Jamdesk : elles passent par fetch(request) et renvoient une 404 depuis votre propre origine. Entre-temps, /docs/* continue de fonctionner (Jamdesk sert les deux préfixes), et c'est précisément ce qui rend le problème facile à manquer.
Solution : ajoutez votre nouveau sous-chemin à PROXY_PATHS. Relancez jamdesk deploy-proxy cloudflare --path <sous-chemin> si le Worker est toujours un modèle non modifié, ou modifiez le tableau à la main si vous l'avez personnalisé.
Wrangler privilégie CLOUDFLARE_API_TOKEN sur sa connexion OAuth, et il ne peut pas démarrer une connexion OAuth tant que ce jeton est défini. Cette commande a besoin d'OAuth pour lister les zones de votre compte : elle s'arrête donc en indiquant d'où vient le jeton, au lieu d'échouer à l'intérieur de wrangler.
Wrangler lit aussi le fichier .env du répertoire courant : le jeton peut donc être défini pour wrangler sans être présent dans votre shell — si echo $CLOUDFLARE_API_TOKEN n'affiche rien, cela ne l'exclut pas. Vérifiez également la présence d'un .env dans le répertoire courant.
Solution : accordez au jeton les permissions account:read et zone:read puis relancez, ou exécutez la commande sans lui :
env -u CLOUDFLARE_API_TOKEN jamdesk deploy-proxy cloudflareSi le jeton provient d'un .env, env -u ne changera rien — exécutez la commande depuis un répertoire dépourvu d'un tel .env, ou déplacez temporairement le fichier.
La CLI affiche vos domaines disponibles avant la sélection de la zone. Si vous voyez « Aucun domaine trouvé » :
- Vérifiez que vous êtes connecté au bon compte Cloudflare
- Vérifiez que votre domaine est ajouté et actif dans le dashboard Cloudflare
- Relancez la CLI et sélectionnez « Non » lorsqu'on vous demande de continuer avec le compte actuel, pour changer de compte
Si vous avez plusieurs comptes Cloudflare :
- Exécutez
jamdesk deploy-proxy cloudflare - Lorsqu'on vous invite à sélectionner un compte, choisissez celui qui contient votre domaine
- Si vous avez besoin d'une connexion totalement différente, sélectionnez « Utiliser une autre connexion »
- La CLI vous déconnectera et vous invitera à vous reconnecter avec les identifiants corrects
Cette erreur signifie que la zone sélectionnée ne correspond pas à votre compte Cloudflare. Deux causes possibles :
- Vous avez sélectionné une zone appartenant à un autre compte
- La zone a été supprimée de Cloudflare
Solution : relancez la CLI et sélectionnez la bonne zone dans la liste, ou basculez vers le compte propriétaire de la zone.
Assurez-vous que votre modèle de route utilise un caractère générique global : yoursite.com/* (et non yoursite.com/docs*). La fonction interne shouldProxy() du Worker gère le filtrage des chemins.
Deux causes courantes :
- Le Worker ne s'exécute pas. Assurez-vous que votre enregistrement DNS est défini sur Proxied (nuage orange) dans Cloudflare. Les Workers ne s'exécutent que sur les enregistrements proxifiés.
- En-tête
X-Forwarded-Hostmanquant. Le Worker doit définir cet en-tête pour que Jamdesk génère les bonnes URL de ressources.
Si vous voyez « Le domaine n'est pas autorisé à servir ce contenu » :
- Vérifiez que votre domaine est enregistré dans le dashboard Jamdesk
- Effectuez la vérification DNS (enregistrement TXT) pour votre domaine
- Assurez-vous que l'en-tête
X-Jamdesk-Forwarded-Hostest défini dans le code de votre Worker - Vérifiez que votre domaine correspond au bon projet
Le domaine doit être vérifié avant que le Worker puisse servir la documentation.
Les Workers ne s'exécutent que sur les enregistrements DNS proxifiés (nuage orange). Si votre enregistrement A est défini sur « DNS only » (nuage gris), les requêtes vont directement à l'origine et contournent entièrement le Worker.
Solution : basculez l'enregistrement A sur proxifié (nuage orange) dans le DNS Cloudflare. Cela s'applique aussi aux sous-domaines : tout enregistrement associé à une route Worker doit être proxifié.
Jamdesk vérifie la propriété en lisant directement les valeurs de vos enregistrements DNS. Le proxy de Cloudflare (nuage orange) masque ces valeurs, ce qui empêche la vérification d'aboutir.
Solution :
- Définissez l'enregistrement DNS sur DNS only (nuage gris)
- Attendez que la vérification se termine (le statut passe à active dans le dashboard)
- Repassez sur Proxied (nuage orange) pour que le Worker fonctionne
En résumé : nuage gris pour vérifier → nuage orange pour servir.
Jamdesk sert le HTML de la documentation avec Cache-Control: no-store, donc Cloudflare ne met pas les pages en cache en périphérie (cf-cache-status: BYPASS). Chaque requête affiche la version actuelle, et les modifications publiées apparaissent immédiatement, sans délai de cache.
Les ressources statiques sous /_next/ et /_jd/ (JavaScript, CSS, polices, images) sont servies avec des en-têtes de cache immutable à longue durée de vie, donc Cloudflare les met en cache en périphérie. Leurs noms de fichiers sont hachés en fonction du contenu, si bien que chaque build produit de nouvelles URL et que les ressources mises à jour sont automatiquement prises en compte. Aucune purge n'est nécessaire.
cacheEverything: true permet à Cloudflare de mettre ces ressources statiques en cache sur la route proxifiée ; cela ne remplace pas le no-store sur le HTML. Pour vider manuellement le cache en périphérie, utilisez Purge Cache de Cloudflare (Caching → Configuration → Purge Everything).
La CLI nécessite wrangler 3.0+. Mettez à jour avec :
npm install -g wrangler@latest