Cloudflare Workers
Proxy /docs requests through a Cloudflare Worker to your Jamdesk site.
A Cloudflare Worker intercepts requests at /docs on your domain, rewrites them to your Jamdesk subdomain, and returns the response -- all at the edge with no origin server required. You can scaffold the Worker automatically with npx jamdesk deploy cloudflare or set it up manually below.
How It Works
The Worker forwards requests to your Jamdesk subdomain while passing along the original domain in the X-Jamdesk-Forwarded-Host header. Jamdesk uses this header to:
- Verify your domain - Ensures only authorized domains can serve your docs
- Apply correct configuration - Uses your domain's settings from the dashboard
This means the Worker is a one-time setup. If you later change your custom domain or configuration in the Jamdesk dashboard, the Worker doesn't need to be updated - all routing decisions are made server-side.
Prerequisites
- A Cloudflare account with your domain configured
- Wrangler CLI v3.0+ installed
- Your Jamdesk subdomain (found in dashboard settings)
Quick Setup with CLI
The fastest way to set up your Cloudflare Worker:
npx jamdesk deploy cloudflare
This interactive command will:
- Check that wrangler 3.0+ is installed
- Verify your Cloudflare account and show available domains
- Auto-detect your project slug from
docs.json - Let you select your target domain from your Cloudflare zones
- Generate all required files
- Optionally deploy to Cloudflare
Account Selection
The CLI verifies you're logged into the correct Cloudflare account before proceeding. If you have access to multiple Cloudflare accounts (common for agencies or teams), you'll be prompted to choose:
Step 2: Verifying Cloudflare account...
✓ Logged in as: you@example.com
? Select the Cloudflare account to use:
❯ Your Personal Account
Company Team Account
[ Switch to different login ]
After selecting an account, only domains from that account will be available for zone selection:
✓ Using account: Company Team Account
✓ Domains: 3 available for Workers routing
Make sure you select the Cloudflare account that has the domain you want to deploy to. Each account has its own set of domains, and you can only create Workers routes for domains in the selected account.
CI/Scripted Deployment
For CI/scripting, use flags to skip prompts:
jamdesk deploy cloudflare --slug myproject --domain example.com --skip-deploy --yes
| Option | Description |
|---|---|
--slug | Jamdesk project slug (skip auto-detection) |
--domain | Target domain (e.g., yoursite.com) |
--path | Path prefix (default: /docs) |
--output-dir | Output directory (default: cloudflare-worker/) |
--skip-deploy | Generate files only, don't deploy |
--force | Overwrite existing directory |
--yes | Skip all prompts (CI mode) |
If you prefer manual setup, continue with the steps below.
Manual Setup
Step 1: Create a Worker
Create a new directory for your Worker and initialize it:
mkdir docs-proxy && cd docs-proxy
npm init -y
Step 2: Add the Worker Code
Create index.js with the following code:
const JAMDESK_HOST = "YOUR_SLUG.jamdesk.app";
// Paths to proxy to Jamdesk
const PROXY_PATHS = [
"/docs", // Documentation pages
"/_next/", // Next.js static assets
"/_jd/", // Jamdesk assets (fonts, images, branding, analytics)
];
export default {
async fetch(request) {
const url = new URL(request.url);
const path = url.pathname;
// Check if this path should be proxied
const shouldProxy = PROXY_PATHS.some(prefix =>
path === prefix || path.startsWith(prefix.endsWith("/") ? prefix : prefix + "/")
);
if (!shouldProxy) {
return fetch(request);
}
// Rewrite the request to Jamdesk
const proxyUrl = new URL(request.url);
proxyUrl.hostname = JAMDESK_HOST;
// 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");
// Required for domain verification
headers.set("X-Jamdesk-Forwarded-Host", url.hostname);
const proxyRequest = new Request(proxyUrl, {
method: request.method,
headers: headers,
body: request.body,
});
// Cache all content types at Cloudflare edge (CF doesn't cache HTML by default).
// Cache duration is controlled by upstream Cache-Control headers.
return fetch(proxyRequest, {
cf: {
cacheEverything: true,
cacheTtlByStatus: { "500-599": 0 },
},
});
},
};Replace YOUR_SLUG with your actual Jamdesk subdomain (e.g., acme if your docs are at acme.jamdesk.app).
The X-Jamdesk-Forwarded-Host header is required. It enables:
- Domain verification (your domain must be registered in the dashboard)
- Correct URL generation for links and assets
- Proper configuration from your dashboard settings
Without this header, requests will be rejected with a 403 error.
Step 3: Configure wrangler.toml
Create wrangler.toml to configure your Worker:
name = "docs-proxy"
main = "index.js"
compatibility_date = "2024-01-01"
# Single catch-all route — the worker handles path filtering internally
routes = [
{ pattern = "yoursite.com/*", zone_name = "yoursite.com" },
]If your site also serves traffic on www.yoursite.com, add a second route so the Worker handles both:
routes = [
{ pattern = "yoursite.com/*", zone_name = "yoursite.com" },
{ pattern = "www.yoursite.com/*", zone_name = "yoursite.com" },
]Step 4: Deploy
Deploy your Worker to Cloudflare:
npx wrangler deploy
Step 5: Verify
Visit https://yoursite.com/docs to confirm your documentation is being served correctly.
Alternative: Service Worker Syntax
If you prefer the older Service Worker syntax (compatible with older Workers):
const JAMDESK_HOST = "YOUR_SLUG.jamdesk.app";
// Paths to proxy to Jamdesk
const PROXY_PATHS = [
"/docs", // Documentation pages
"/_next/", // Next.js static assets
"/_jd/", // Jamdesk assets (fonts, images, branding, analytics)
];
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
const path = url.pathname;
// Check if this path should be proxied
const shouldProxy = PROXY_PATHS.some(prefix =>
path === prefix || path.startsWith(prefix.endsWith("/") ? prefix : prefix + "/")
);
if (!shouldProxy) {
return fetch(request);
}
const proxyUrl = new URL(request.url);
proxyUrl.hostname = JAMDESK_HOST;
const headers = new Headers(request.headers);
headers.set("Host", JAMDESK_HOST);
headers.set("X-Forwarded-Host", url.hostname);
headers.set("X-Forwarded-Proto", "https");
// Required for domain verification
headers.set("X-Jamdesk-Forwarded-Host", url.hostname);
// Cache all content types at Cloudflare edge (CF doesn't cache HTML by default).
// Cache duration is controlled by upstream Cache-Control headers.
return fetch(new Request(proxyUrl, {
method: request.method,
headers: headers,
body: request.body,
}), {
cf: {
cacheEverything: true,
cacheTtlByStatus: { "500-599": 0 },
},
});
}Troubleshooting
The CLI shows your available domains before zone selection. If you see "No domains found":
- Verify you're logged into the correct Cloudflare account
- Check that your domain is added and active in the Cloudflare dashboard
- Run the CLI again and select "No" when asked to continue with the current account to switch accounts
If you have multiple Cloudflare accounts:
- Run
jamdesk deploy cloudflare - When prompted to select an account, choose the one with your domain
- If you need a completely different login, select "Switch to different login"
- The CLI will log you out and prompt you to log in with the correct credentials
This error means the selected zone doesn't match your Cloudflare account. Either:
- You selected a zone that belongs to a different account
- The zone was removed from Cloudflare
Fix: Re-run the CLI and select the correct zone from the list, or switch to the account that owns the zone.
Ensure your route pattern uses a catch-all: yoursite.com/* (not just yoursite.com/docs*). The Worker's internal shouldProxy() function handles path filtering.
Two common causes:
- Worker not running — Ensure your DNS record is set to Proxied (orange cloud) in Cloudflare. Workers only run on proxied records.
- Missing
X-Forwarded-Hostheader — The Worker must set this header so Jamdesk generates correct asset URLs.
If you see "Domain is not authorized to serve this content":
- Verify your domain is registered in the Jamdesk dashboard
- Complete DNS verification (TXT record) for your domain
- Ensure the
X-Jamdesk-Forwarded-Hostheader is set in your Worker code - Check that your domain maps to the correct project
The domain must be verified before the Worker can serve documentation.
Workers only run on proxied (orange cloud) DNS records. If your A record is set to "DNS only" (gray cloud), requests go straight to the origin and skip the Worker entirely.
Fix: Toggle the A record to proxied (orange cloud) in Cloudflare DNS. Same applies to subdomains — any record with a Worker route must be proxied.
Jamdesk verifies ownership by reading your DNS record values directly. Cloudflare's proxy (orange cloud) masks these values, so verification can't complete.
Fix:
- Set the DNS record to DNS only (gray cloud)
- Wait for verification to complete (status changes to active in the dashboard)
- Switch back to Proxied (orange cloud) so the Worker runs
Short version: gray cloud to verify → orange cloud to serve.
The Worker enables Cloudflare edge caching for all content types, including HTML. Cache duration is controlled by the upstream response headers — HTML is cached for 5 minutes, while static assets like images and fonts are cached much longer.
After a deploy, updated content will appear within 5 minutes without any action needed. If you need to force-refresh immediately, use Cloudflare's Purge Cache feature in your dashboard (Caching → Configuration → Purge Everything).
The CLI requires wrangler 3.0+. Update with:
npm install -g wrangler@latest