---
title: Cloudflare Workers
description: "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."
---

> **For AI agents:** the complete documentation index is at [llms.txt](/docs/llms.txt). Append `.md` to any page URL for its markdown version.

Um [Cloudflare Worker](https://workers.cloudflare.com/) 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](https://developers.cloudflare.com/workers/wrangler/install-and-update/) 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 `AAAA` de placeholder apontando para `100::` e defina-o como proxied.

## Configuração rápida com CLI

A maneira mais rápida de configurar seu Cloudflare Worker:

```bash
npx jamdesk deploy-proxy cloudflare
```

Este comando interativo irá:
1. Verificar se o wrangler 3.0+ está instalado
2. Verificar sua conta Cloudflare e mostrar os domínios disponíveis
3. Resolver seu subdomínio Jamdesk a partir do projeto vinculado em `docs.json`
4. Permitir selecionar o domínio de destino entre suas zonas Cloudflare
5. Gerar todos os arquivos necessários
6. 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:

```bash
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:

```bash
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:

```bash
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:

```javascript index.js
/**
 * 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);
  },
};
```

<Note>
Substitua `YOUR_SLUG` pelo seu subdomínio Jamdesk real (por exemplo, `acme` se sua documentação estiver em `acme.jamdesk.app`).
</Note>

<Note>
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.
</Note>

<Warning>
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.
</Warning>

### Etapa 3: configurar wrangler.toml

Crie `wrangler.toml` para configurar seu Worker:

```toml wrangler.toml
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" },
]
```

<Note>
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.
</Note>

<Tip>
Se o seu site também servir tráfego em `www.yoursite.com`, adicione uma segunda rota para que o Worker gerencie ambos:

```toml
routes = [
  { pattern = "yoursite.com/*", zone_name = "yoursite.com" },
  { pattern = "www.yoursite.com/*", zone_name = "yoursite.com" },
]
```
</Tip>

### Etapa 4: fazer deploy

Faça o deploy do seu Worker na Cloudflare:

```bash
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

<Accordion title="Novo subcaminho retorna 404 após renomeação no dashboard">
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.
</Accordion>

<Accordion title="“Você está conectado com um API Token. Desative CLOUDFLARE_API_TOKEN…”">
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:

```bash
env -u CLOUDFLARE_API_TOKEN jamdesk deploy-proxy cloudflare
```

Se 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.
</Accordion>

<Accordion title="Nenhum domínio encontrado nesta conta">
A CLI mostra os domínios disponíveis antes da seleção da zona. Se você vir “Nenhum domínio encontrado”:
1. Verifique se você está conectado à conta Cloudflare correta
2. Confirme se o domínio foi adicionado e está ativo no dashboard da Cloudflare
3. Execute a CLI novamente e selecione “Não” quando for perguntado se deseja continuar com a conta atual para trocar de conta
</Accordion>

<Accordion title="Conta Cloudflare incorreta">
Se você tiver várias contas Cloudflare:
1. Execute `jamdesk deploy-proxy cloudflare`
2. Quando solicitado a selecionar uma conta, escolha aquela que contém o seu domínio
3. Se precisar de um login completamente diferente, selecione **"Switch to different login"**
4. A CLI desconectará você e solicitará o login com as credenciais corretas
</Accordion>

<Accordion title="Zona não encontrada durante o deploy">
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.
</Accordion>

<Accordion title="Erros 404 nas páginas de documentação">
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.
</Accordion>

<Accordion title="Os assets não carregam corretamente">
Duas causas comuns:

1. **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.
2. **Cabeçalho `X-Forwarded-Host` ausente.** O Worker deve definir este cabeçalho para que o Jamdesk gere URLs corretas para os assets.
</Accordion>

<Accordion title="Erro 403: domínio não autorizado">
Se você vir “O domínio não está autorizado a servir este conteúdo”:

1. Verifique se o domínio está registrado no dashboard do Jamdesk
2. Conclua a verificação DNS (registro TXT) do seu domínio
3. Certifique-se de que o cabeçalho `X-Jamdesk-Forwarded-Host` esteja definido no código do Worker
4. 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.
</Accordion>

<Accordion title="O Worker não é acionado no domínio raiz (apex)">
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.
</Accordion>

<Accordion title="Verificação de domínio travada em Pending">
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:**
1. Defina o registro DNS como **DNS only** (nuvem cinza)
2. Aguarde a conclusão da verificação (o status muda para **active** no dashboard)
3. Volte para **Proxied** (nuvem laranja) para que o Worker seja executado

Resumo: **nuvem cinza** para verificar → **nuvem laranja** para servir.
</Accordion>

<Accordion title="Como funciona o cache">
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).
</Accordion>

<Accordion title="Versão do Wrangler muito antiga">
A CLI requer o wrangler 3.0+. Atualize com:

```bash
npm install -g wrangler@latest
```
</Accordion>

## O que vem a seguir?

<Columns cols={3}>
  <Card title="Somente domínio personalizado" icon="eye-slash" href="/pt/deploy/custom-domain-only">
    Impedir que seu subdomínio responda diretamente
  </Card>
  <Card title="Domínios personalizados" icon="globe" href="/pt/deploy/custom-domains">
    Verificar o DNS e solucionar problemas
  </Card>
  <Card title="Hospedagem em subcaminho" icon="folder-tree" href="/pt/deploy/subpath-hosting">
    Servir a documentação em /docs
  </Card>
</Columns>