tursodatabase/turso · error · DatabaseError
overwriting the 'Host' header is not supported
Error message
overwriting the 'Host' header is not supported
What it means
DatabaseError thrown while building request headers when a per-query requestHeaders object (QueryOptions.requestHeaders) contains a Host key, matched case-insensitively. fetch() classifies Host as a forbidden header and would silently drop the override, so the driver throws to make the no-op visible instead of letting routing logic quietly not work.
Source
Thrown at serverless/javascript/src/protocol.ts:243
*/
requestHeaders?: Record<string, string>;
}
function buildHeaders(ctx: HttpContext): Record<string, string> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (ctx.authToken) {
headers['Authorization'] = `Bearer ${ctx.authToken}`;
}
if (ctx.remoteEncryptionKey) {
headers[ENCRYPTION_KEY_HEADER] = ctx.remoteEncryptionKey;
}
for (const [name, value] of Object.entries(ctx.requestHeaders ?? {})) {
// `Host` is a forbidden fetch header and would be silently dropped —
// throw instead so the caller learns the override never takes effect.
if (name.toLowerCase() === 'host') {
throw new DatabaseError("overwriting the 'Host' header is not supported");
}
headers[name] = value;
}
return headers;
}
function buildFetchOptions(ctx: HttpContext, body: string, signal?: AbortSignal): RequestInit {
return {
method: 'POST',
headers: buildHeaders(ctx),
body,
signal,
};
}
/** Per-query options. Override the session-level defaults for a single call. */
export interface QueryOptions {
/** Per-query timeout in milliseconds. Overrides defaultQueryTimeout for this call. */View on GitHub (pinned to bad083fafb)
Solutions
- Route by changing the connection's url (connect({ url })), never by overriding Host
- Strip the Host key before passing headers: filter out keys whose lowercase name is 'host'
- Use custom x-* headers for routing metadata — those pass through fine
Example fix
// before
await db.all(sql, { requestHeaders: { ...incomingHeaders } }); // incomingHeaders contains Host
// after
const headers = Object.fromEntries(
Object.entries(incomingHeaders).filter(([k]) => k.toLowerCase() !== "host"),
);
await db.all(sql, { requestHeaders: headers }); Defensive patterns
Strategy: validation
Validate before calling
const stripHost = (h: Record<string, string> = {}): Record<string, string> =>
Object.fromEntries(
Object.entries(h).filter(([k]) => k.toLowerCase() !== "host"),
);
await db.all(sql, { requestHeaders: stripHost(incomingHeaders) }); Prevention
- Route by the connection url, never by the Host header — fetch forbids it
- Whitelist header keys when forwarding; never spread a full incoming header set into queryOptions
- Prefer x-* custom headers for routing metadata
When it happens
Trigger: Passing { requestHeaders: { Host: 'db.example.com' } } as queryOptions to run/get/all/exec/batch/pragma or statement methods; spreading an incoming request's full header set (which includes host) into queryOptions; case variants 'host'/'HOST' which are rejected the same way.
Common situations: Proxy or multi-tenant routing setups that try to select a backend via the Host header; middleware that forwards original client headers to the database request; header constants shared across services that include host.
Related errors
- overwriting the 'Host' header is not supported
- retryFetch: attempts must be a finite integer >= 1, got ${at
- reader is null
- remoteWritesExperimental requires a non-null URL
- sync is disabled as database was opened without sync support
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/69908f2af503c4b4.
Report an issue: GitHub.