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
- Send the absolute URL in the request-line (proxy form: GET http://host/path HTTP/1.1)
- Otherwise set a valid Host header on the request before it reaches the proxy
- 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
- Use standard proxy-aware agents instead of raw requests
- Always set absolute URLs or Host when crafting proxy requests
- Validate requests at the edge before forwarding
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
- CONNECT target is missing.
- ZCode profiles can only open the app; agent arguments are no
- Claude Design profiles can only be opened from CCR Desktop.
- HTTP + response.status + from CCR remote sync
- The CCR artifact endpoint returned HTTP ${response.status}.
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/e2bb67770d6a6de1.
Report an issue: GitHub.