musistudio/claude-code-router · error · Error

Proxy request is missing Host header.

Error message

Proxy request is missing Host header.

What it means

An HTTP proxy request arrived with a relative URL (path-only form) and no Host header, so the proxy cannot reconstruct the absolute target URL. Proxy-style requests require either an absolute URL or an origin-form path plus a Host header.

Source

Thrown at packages/core/src/proxy/service.ts:1346

    return normalizedHost.endsWith(normalizedPattern);
  }
  return false;
}

function buildGatewayUrl(config: AppConfig, targetUrl: URL): URL {
  const gatewayHost = config.gateway.host === "0.0.0.0" ? "127.0.0.1" : config.gateway.host;
  return new URL(`${targetUrl.pathname}${targetUrl.search}`, `http://${gatewayHost}:${config.gateway.port}`);
}

function resolveRequestUrl(request: IncomingMessage, defaultProtocol: "http:" | "https:"): URL {
  const rawUrl = request.url || "/";
  if (/^https?:\/\//i.test(rawUrl)) {
    return new URL(rawUrl);
  }

  const host = readHeader(request.headers.host);
  if (!host) {
    throw new Error("Proxy request is missing Host header.");
  }
  return new URL(`${defaultProtocol}//${host}${rawUrl.startsWith("/") ? rawUrl : `/${rawUrl}`}`);
}

function parseConnectTarget(value: string | undefined): { hostname: string; port: number } {
  if (!value) {
    throw new Error("CONNECT target is missing.");
  }
  const parsed = new URL(`http://${value}`);
  return {
    hostname: parsed.hostname,
    port: parsed.port ? Number(parsed.port) : 443
  };
}

function proxyEndpoint(config: AppConfig): string {
  const host = config.proxy.host === "0.0.0.0" ? "127.0.0.1" : config.proxy.host;
  return `http://${host}:${config.proxy.port}`;

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Send the absolute URL in the request-line (proxy form: GET http://host/path HTTP/1.1)
  2. Otherwise set a valid Host header on the request before it reaches the proxy
  3. If writing a custom client, use a proper proxy-aware agent (e.g. http-proxy-agent) instead of raw sockets

Example fix

// before
http.request({ host: "127.0.0.1", port: proxyPort, path: "/api/v1" }); // throws

// after
http.request({ host: "127.0.0.1", port: proxyPort, path: "http://example.com/api/v1", headers: { host: "example.com" } });
Defensive patterns

Strategy: validation

Validate before calling

const url = rawUrl.startsWith("http") ? rawUrl : `http://${headers.host}${rawUrl}`;
if (!headers.host && !/^https?:\/\//i.test(rawUrl)) {
  return badRequest();
}
await proxyRequest(rawUrl, headers);

Type guard

function hasValidProxyTarget(rawUrl: string, headers: IncomingHttpHeaders): boolean {
  return /^https?:\/\//i.test(rawUrl) || typeof headers.host === "string" && headers.host.length > 0;
}

Try / catch

try { await handleProxyRequest(req); } catch (e) { if (e.message.includes("missing Host header")) return respond(400, "Host header required"); throw e; }

Prevention

When it happens

Trigger: Calling the proxy with a plain HTTP request whose request-line is origin-form (e.g. GET /path) and whose headers lack Host — e.g. hand-rolled Node http.request through the proxy without setting host, or a client that strips Host.

Common situations: Custom test clients using http.request({path: "/x"}) without host; proxying HTTP/1.0-style requests that omit Host; middleware that deletes the host header; malformed curl invocations (-H 'Host:').

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/e2bb67770d6a6de1. Report an issue: GitHub.