Written by: Vineeth Voruganti

How honcho.dev/docs and docs.honcho.dev are wired up to Mintlify via a Cloudflare Worker.

Shape of the setup

Mintlify hosts our docs at plasticlabs.mintlify.dev. We don’t want users to see that URL — we want docs to live under our apex domain. A Cloudflare Worker handles two things:

  1. Redirects docs.honcho.dev/*honcho.dev/docs/* (301), so the legacy subdomain still works.
  2. Reverse-proxies honcho.dev/docs* and honcho.dev/mintlify-assets* to plasticlabs.mintlify.dev, rewriting the Host header so Mintlify serves the right project and assets resolve correctly under our apex.

/.well-known/* is passed through untouched so Vercel’s domain verification on honcho.dev keeps working.

Worker

worker.js:

addEventListener("fetch", (event) => {
  event.respondWith(handleRequest(event.request));
});
 
async function handleRequest(request) {
  try {
    const urlObject = new URL(request.url);
 
    // Redirect docs.honcho.dev/* → honcho.dev/docs/*
    if (urlObject.hostname === "docs.honcho.dev") {
      const newUrl =
        "https://honcho.dev/docs" + urlObject.pathname + urlObject.search;
      return Response.redirect(newUrl, 301);
    }
 
    // Allow Vercel verification paths through
    if (urlObject.pathname.startsWith("/.well-known/")) {
      return await fetch(request);
    }
 
    // Proxy /docs* and /mintlify-assets* → Mintlify
    if (/^\/(docs|mintlify-assets)/.test(urlObject.pathname)) {
      const DOCS_URL = "plasticlabs.mintlify.dev";
      const CUSTOM_URL = "honcho.dev";
 
      let url = new URL(request.url);
      url.hostname = DOCS_URL;
 
      let proxyRequest = new Request(url, request);
      proxyRequest.headers.set("Host", DOCS_URL);
      proxyRequest.headers.set("X-Forwarded-Host", CUSTOM_URL);
      proxyRequest.headers.set("X-Forwarded-Proto", "https");
      proxyRequest.headers.set(
        "CF-Connecting-IP",
        request.headers.get("CF-Connecting-IP"),
      );
 
      return await fetch(proxyRequest);
    }
  } catch (error) {
    return await fetch(request);
  }
 
  return await fetch(request);
}

wrangler.toml

name = "honcho-docs-proxy"
main = "worker.js"
compatibility_date = "2024-01-01"
 
routes = [
  { pattern = "honcho.dev/docs*", zone_name = "honcho.dev" },
  { pattern = "docs.honcho.dev/*", zone_name = "honcho.dev" }
]

Deploy with wrangler deploy.

Gotchas

A few things weren’t obvious from the Mintlify docs and cost time:

  • The docs.honcho.dev route needs a DNS record to exist. Cloudflare Workers only match a route if the hostname resolves in the zone. I added a dummy A record for docs.honcho.dev (any IP — 192.0.2.1 works) and enabled the orange-cloud proxy so Cloudflare actually sees the request and runs the Worker.
  • Mintlify has to be rebuilt manually after the proxy goes live for it to pick up the custom domain and emit correct asset URLs. Trigger a rebuild from the Mintlify dashboard once the Worker is deployed; otherwise asset paths come back pointing at plasticlabs.mintlify.dev.
  • The mintlify-assets path prefix has to be proxied alongside /docs — Mintlify’s bundler emits asset references under that prefix, and skipping it produces a docs site with broken CSS/JS.

Why a Worker

We don’t host the docs ourselves — Mintlify does, at plasticlabs.mintlify.dev. The apex honcho.dev is served by Vercel (marketing site), so something has to split traffic by path and forward /docs* to Mintlify’s origin while everything else continues to Vercel. The Cloudflare Worker is that splitter: it sits in front of the zone, reverse-proxies the docs paths to Mintlify, and leaves the rest of the site alone.