{"record":{"id":"9e02b04f012b8c2a","repo":"tursodatabase/turso","slug":"overwriting-the-host-header-is-not-supported-9e02b0","errorCode":null,"errorMessage":"overwriting the 'Host' header is not supported","messagePattern":"overwriting the 'Host' header is not supported","errorType":"exception","errorClass":"DatabaseError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/session.ts","lineNumber":99,"sourceCode":" * A database session that manages the connection state and baton.\n * \n * Each session maintains its own connection state and can execute SQL statements\n * independently without interfering with other sessions.\n */\nexport class Session {\n  private config: SessionConfig;\n  private baton: string | null = null;\n  private baseUrl: string;\n  // Cached autocommit status from the server's last `get_autocommit` answer.\n  // A fresh connection is in autocommit (not in a transaction).\n  private autocommit: boolean = true;\n\n  constructor(config: SessionConfig) {\n    for (const name of Object.keys(config.requestHeaders ?? {})) {\n      // `Host` is a forbidden fetch header and would be silently dropped —\n      // reject it up front so the caller learns the override never takes effect.\n      if (name.toLowerCase() === 'host') {\n        throw new DatabaseError(\"overwriting the 'Host' header is not supported\");\n      }\n    }\n    this.config = config;\n    this.baseUrl = normalizeUrl(config.url);\n  }\n\n  private httpContext(queryOptions?: QueryOptions): HttpContext {\n    // Per-query headers are merged over the session-level ones, so a query\n    // can override a header the session sets (and both override the\n    // standard headers).\n    let requestHeaders = this.config.requestHeaders;\n    if (queryOptions?.requestHeaders) {\n      requestHeaders = { ...requestHeaders, ...queryOptions.requestHeaders };\n    }\n    return {\n      url: this.baseUrl,\n      authToken: this.config.authToken,\n      remoteEncryptionKey: this.config.remoteEncryptionKey,","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/serverless/javascript/src/session.ts#L81-L117","documentation":"DatabaseError thrown by the Session constructor when the session-level requestHeaders config contains a Host key, matched case-insensitively. Because connect()/new Session() is cheap and lazy (no I/O until the first query), this check runs eagerly at construction so the mistake surfaces before any request — fetch would otherwise silently drop the forbidden Host header on every call.","triggerScenarios":"connect({ url, authToken, requestHeaders: { Host: 'tenant.example.com' } }); forwarding an inbound request's full header set (which includes host) into the client config; lowercase 'host' or uppercase 'HOST' keys, all rejected identically.","commonSituations":"Multi-tenant routing attempts via the Host header; shared header constants that bundle host with real overrides; copy-pasted proxy configuration blocks adapted into requestHeaders.","solutions":["Remove Host from requestHeaders and change the connection url to route to the intended host","When forwarding headers, whitelist only the keys you need (authorization, x-*) instead of spreading all of them","Remember per-query QueryOptions.requestHeaders carries the same restriction — apply the same filtering there"],"exampleFix":"// before\nconst db = connect({ url, requestHeaders: { Host: \"tenant-7.internal\" } });\n\n// after\nconst db = connect({ url: \"https://tenant-7.internal\", requestHeaders: { \"x-tenant-id\": \"7\" } });","handlingStrategy":"validation","validationCode":"const stripHost = (h: Record<string, string> = {}): Record<string, string> =>\n  Object.fromEntries(\n    Object.entries(h).filter(([k]) => k.toLowerCase() !== \"host\"),\n  );\n\nconst db = connect({ url, authToken, requestHeaders: stripHost(forwardedHeaders) });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Route by changing url, never by setting Host — the constructor rejects it eagerly at connect() time","Whitelist header keys when forwarding inbound headers into the client config","Check both session-level requestHeaders and per-query QueryOptions.requestHeaders for 'host' in any casing"],"tags":["http-headers","configuration","fetch","javascript"],"backgroundTag":"forbidden-http-header","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}