Cloudflare Workers
Encaminhe solicitações /docs por um Cloudflare Worker para seu site de documentação Jamdesk, com configuração de rotas, Worker e cache na borda.
Um Cloudflare Worker intercepta solicitações em /docs no seu domínio, reescreve-as para o seu subdomínio Jamdesk e retorna a resposta, tudo na borda e sem exigir um servidor de origem. Você pode criar a estrutura do Worker automaticamente com npx jamdesk deploy-proxy cloudflare ou configurá-lo manualmente abaixo.
Como funciona
O Worker encaminha as solicitações para o seu subdomínio Jamdesk e envia o seu domínio no cabeçalho X-Jamdesk-Forwarded-Host, que o Jamdesk usa para verificar o domínio e aplicar suas configurações. É uma configuração única — se você alterar o domínio ou a configuração posteriormente no dashboard, não será necessário atualizar o Worker.
Pré-requisitos
- Uma conta Cloudflare com o seu domínio configurado
- Wrangler CLI v3.0+ instalado
- Seu subdomínio Jamdesk (encontrado nas configurações do dashboard)
- Seu domínio personalizado adicionado ao projeto no dashboard do Jamdesk (o Worker retorna 403 até que o domínio seja registrado e verificado)
- Um registro DNS proxied (nuvem laranja) no hostname que hospeda sua documentação. Workers só são executados em registros com proxy, portanto, um domínio que não hospeda mais nada ainda precisa de um — adicione um registro
AAAAde placeholder apontando para100::e defina-o como proxied.
Configuração rápida com CLI
A maneira mais rápida de configurar seu Cloudflare Worker:
npx jamdesk deploy-proxy cloudflare
Este comando interativo irá:
- Verificar se o wrangler 3.0+ está instalado
- Verificar sua conta Cloudflare e mostrar os domínios disponíveis
- Resolver seu subdomínio Jamdesk a partir do projeto vinculado em
docs.json - Permitir selecionar o domínio de destino entre suas zonas Cloudflare
- Gerar todos os arquivos necessários
- Opcionalmente fazer o deploy na Cloudflare
Se você tiver acesso a várias contas Cloudflare (algo comum para agências ou equipes), a CLI solicitará que você escolha uma antes da seleção da zona. Escolha a conta proprietária do domínio no qual você está fazendo o deploy — as rotas de Workers só podem ser criadas para domínios da conta selecionada.
Configuração não interativa
Para executar sem prompts — em CI ou a partir de um script — passe as respostas como flags:
jamdesk deploy-proxy cloudflare --slug myproject --domain example.com --yes
--yes gera os arquivos do Worker e para. Ele nunca faz o deploy: a zona é presumida a partir do seu domínio, em vez de ser confirmada na sua conta Cloudflare, portanto, publicar essa configuração fica como uma etapa explícita. Finalize com:
cd cloudflare-worker
npx wrangler deploy
Com --yes, a CLI não pode solicitar informações, então precisa saber seu subdomínio. Ela o lê do projeto vinculado em docs.json; se o link estiver ausente (execute jamdesk deploy uma vez para adicioná-lo), passe --slug explicitamente ou o comando será interrompido em vez de fazer uma suposição.
Se o diretório de saída já existir, --yes interromperá a execução em vez de substituí-lo. Adicione --force para sobrescrever ou --output-dir para gravar em outro local.
| Opção | Descrição |
|---|---|
--slug | Seu subdomínio Jamdesk, o X em X.jamdesk.app (ignora a detecção automática) |
--domain | Domínio de destino (por exemplo, yoursite.com) |
--path | Prefixo do caminho; deve corresponder exatamente ao subcaminho do seu dashboard (padrão: /docs) |
--output-dir | Diretório de saída (padrão: cloudflare-worker/) |
--skip-deploy | Ignora o prompt “fazer deploy agora?” em uma execução interativa (--yes nunca faz deploy) |
--force | Sobrescreve o diretório de saída se ele já existir |
--yes | Responde a todos os prompts com o valor padrão (modo CI). Nunca faz deploy nem sobrescreve um diretório existente — combine com --force para isso |
Se preferir uma configuração manual, continue com as etapas abaixo.
Configuração manual
Etapa 1: criar um Worker
Crie um novo diretório para o Worker e inicialize-o:
mkdir docs-proxy && cd docs-proxy
npm init -y
Etapa 2: adicionar o código do Worker
Crie index.js com o código a seguir:
/**
* 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);
},
};Substitua YOUR_SLUG pelo seu subdomínio Jamdesk real (por exemplo, acme se sua documentação estiver em acme.jamdesk.app).
Se você usar um subcaminho personalizado do dashboard em vez do padrão /docs, substitua "/docs" pelo seu subcaminho em PROXY_PATHS e na verificação de correspondência exata dentro de shouldProxy(). A flag --path da CLI faz isso por você ao gerar o arquivo, mas somente em um template não modificado; gerar novamente substitui todas as entradas personalizadas adicionadas manualmente a PROXY_PATHS. Se você personalizou este Worker, edite as entradas /docs diretamente.
O cabeçalho X-Jamdesk-Forwarded-Host é obrigatório, e um cabeçalho ausente falha silenciosamente — as solicitações continuam funcionando, mas as páginas são servidas com noindex e links canônicos apontando para YOUR_SLUG.jamdesk.app em vez do seu domínio, portanto, os mecanismos de pesquisa nunca indexam sua documentação. Um 403 é o problema oposto: o cabeçalho está presente, mas indica um domínio que não está registrado e ativo para este projeto.
Etapa 3: configurar wrangler.toml
Crie wrangler.toml para configurar seu 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" },
]Se o seu login da Cloudflare tiver acesso a mais de uma conta, adicione também account_id = "<your account id>" — caso contrário, wrangler deploy será interrompido em vez de adivinhar em qual conta fazer o deploy. npx wrangler whoami lista os IDs das suas contas.
Se o seu site também servir tráfego em www.yoursite.com, adicione uma segunda rota para que o Worker gerencie ambos:
routes = [
{ pattern = "yoursite.com/*", zone_name = "yoursite.com" },
{ pattern = "www.yoursite.com/*", zone_name = "yoursite.com" },
]Etapa 4: fazer deploy
Faça o deploy do seu Worker na Cloudflare:
npx wrangler deploy
Etapa 5: verificar
Acesse https://yoursite.com/docs para confirmar que sua documentação está sendo servida corretamente.
Solução de problemas
Se você renomeou o subcaminho no dashboard (por exemplo, de /docs → /help), mas o PROXY_PATHS do Worker ainda lista apenas /docs, as solicitações para /help/* nunca chegam ao Jamdesk: elas passam para fetch(request) e retornam 404 na sua própria origem. /docs/* continua funcionando nesse intervalo (o Jamdesk serve ambos os prefixos), exatamente por isso é fácil não perceber o problema.
Correção: adicione o novo subcaminho a PROXY_PATHS. Execute novamente jamdesk deploy-proxy cloudflare --path <subpath> se o Worker ainda for um template não modificado ou edite a matriz manualmente se você a tiver personalizado.
O Wrangler dá preferência a CLOUDFLARE_API_TOKEN em vez do login OAuth e não consegue iniciar um login OAuth enquanto esse token estiver definido. Este comando precisa do OAuth para listar as zonas da sua conta, então é interrompido e informa a localização do token em vez de falhar dentro do wrangler.
O Wrangler também lê .env do diretório em que você executa o comando, portanto, o token pode estar definido para o wrangler sem estar no seu shell — o fato de echo $CLOUDFLARE_API_TOKEN não exibir nada não o descarta. Verifique também se há um .env no diretório atual.
Correção: conceda ao token account:read e zone:read e execute novamente, ou execute sem ele:
env -u CLOUDFLARE_API_TOKEN jamdesk deploy-proxy cloudflareSe o token vier de um .env, env -u não ajudará — execute o comando a partir de um diretório que não contenha esse .env ou mova o arquivo temporariamente.
A CLI mostra os domínios disponíveis antes da seleção da zona. Se você vir “Nenhum domínio encontrado”:
- Verifique se você está conectado à conta Cloudflare correta
- Confirme se o domínio foi adicionado e está ativo no dashboard da Cloudflare
- Execute a CLI novamente e selecione “Não” quando for perguntado se deseja continuar com a conta atual para trocar de conta
Se você tiver várias contas Cloudflare:
- Execute
jamdesk deploy-proxy cloudflare - Quando solicitado a selecionar uma conta, escolha aquela que contém o seu domínio
- Se precisar de um login completamente diferente, selecione "Switch to different login"
- A CLI desconectará você e solicitará o login com as credenciais corretas
Este erro significa que a zona selecionada não corresponde à sua conta Cloudflare. Uma destas situações ocorreu:
- Você selecionou uma zona pertencente a outra conta
- A zona foi removida da Cloudflare
Correção: execute a CLI novamente e selecione a zona correta na lista ou troque para a conta proprietária da zona.
Certifique-se de que o padrão da rota usa um catch-all: yoursite.com/* (não apenas yoursite.com/docs*). A função interna shouldProxy() do Worker gerencia a filtragem do caminho.
Duas causas comuns:
- O Worker não está em execução. Certifique-se de que seu registro DNS esteja definido como Proxied (nuvem laranja) na Cloudflare. Workers só são executados em registros com proxy.
- Cabeçalho
X-Forwarded-Hostausente. O Worker deve definir este cabeçalho para que o Jamdesk gere URLs corretas para os assets.
Se você vir “O domínio não está autorizado a servir este conteúdo”:
- Verifique se o domínio está registrado no dashboard do Jamdesk
- Conclua a verificação DNS (registro TXT) do seu domínio
- Certifique-se de que o cabeçalho
X-Jamdesk-Forwarded-Hostesteja definido no código do Worker - Verifique se o domínio está associado ao projeto correto
O domínio precisa ser verificado antes que o Worker possa servir a documentação.
Workers só são executados em registros DNS proxied (nuvem laranja). Se o registro A estiver definido como “DNS only” (nuvem cinza), as solicitações vão diretamente para a origem e ignoram completamente o Worker.
Correção: alterne o registro A para proxied (nuvem laranja) no DNS da Cloudflare. O mesmo se aplica a subdomínios: qualquer registro com uma rota de Worker precisa usar proxy.
O Jamdesk verifica a propriedade lendo diretamente os valores dos seus registros DNS. O proxy da Cloudflare (nuvem laranja) oculta esses valores, portanto, a verificação não pode ser concluída.
Correção:
- Defina o registro DNS como DNS only (nuvem cinza)
- Aguarde a conclusão da verificação (o status muda para active no dashboard)
- Volte para Proxied (nuvem laranja) para que o Worker seja executado
Resumo: nuvem cinza para verificar → nuvem laranja para servir.
O Jamdesk serve o HTML da documentação com Cache-Control: no-store, portanto, a Cloudflare não armazena as páginas em cache na borda (cf-cache-status: BYPASS). Cada solicitação renderiza a versão atual, e as alterações publicadas aparecem imediatamente, sem atraso de cache.
Os assets estáticos em /_next/ e /_jd/ (JavaScript, CSS, fontes, imagens) são servidos com cabeçalhos de cache immutable de longa duração, portanto, a Cloudflare os armazena em cache na borda. Os nomes dos arquivos têm hash de conteúdo, então cada build produz novas URLs e os assets atualizados são obtidos automaticamente. Não é necessário fazer purge.
cacheEverything: true permite que a Cloudflare armazene esses assets estáticos em cache na rota com proxy; isso não substitui o no-store do HTML. Para limpar manualmente o cache da borda, use Purge Cache da Cloudflare (Caching → Configuration → Purge Everything).
A CLI requer o wrangler 3.0+. Atualize com:
npm install -g wrangler@latest