[PR #8579] fix: remove request header leak from before-hook early return #16322

Open
opened 2026-04-13 10:29:36 -05:00 by GiteaMirror · 0 comments
Owner

Original Pull Request: https://github.com/better-auth/better-auth/pull/8579

State: open
Merged: No


Summary

When a hooks.before middleware short-circuits (returns early without a context key), the response incorrectly receives the request's headers — including Content-Length, Host, Cookie, etc. This causes net::ERR_CONTENT_LENGTH_MISMATCH in browsers.

A companion defensive fix in better-call is at https://github.com/better-auth/better-call/pull/120.

Root Cause

In to-auth-endpoints.ts lines 161-172, when a before hook returns a non-context value (early return), the code was:

return context?.asResponse
    ? toResponse(before, { headers: context?.headers })  // ← request headers!
    : context?.returnHeaders
        ? { headers: context?.headers, response: before } // ← request headers!
        : before;

At this point in execution, internalContext.context.responseHeaders is undefined (line 117) — no response headers have been accumulated. context?.headers contains the request headers (set at line 121 from new Headers(context?.headers)). Passing these to toResponse() grafts request headers onto the response.

Fix

Remove context?.headers from the early-return path entirely:

return context?.asResponse
    ? toResponse(before)          // no headers — toResponse sets its own
    : context?.returnHeaders
        ? { headers: new Headers(), response: before }  // empty response headers
        : before;

This is correct because:

  1. No response headers exist at this point (responseHeaders is undefined)
  2. The hook's return value IS the response — it doesn't need request headers grafted onto it
  3. toResponse() sets appropriate headers (like Content-Type) based on the data type

Test plan

  • New test: before hook early return with asResponse: true does not leak request headers
  • New test: before hook early return with returnHeaders: true does not leak request headers
  • Verify content-type: application/json is correctly set on early-return responses
  • All 36 existing + new tests pass

Summary by cubic

Fixes a bug in better-auth where early returns in before hooks leaked request headers into responses, causing browser net::ERR_CONTENT_LENGTH_MISMATCH errors.

  • Bug Fixes
    • Stop passing context.headers to toResponse() on early return.
    • Return empty headers when returnHeaders: true.
    • Add tests to ensure no header leak and correct content-type is set.

Written for commit 724a978259. Summary will update on new commits.

**Original Pull Request:** https://github.com/better-auth/better-auth/pull/8579 **State:** open **Merged:** No --- ## Summary When a `hooks.before` middleware short-circuits (returns early without a `context` key), the response incorrectly receives the **request's** headers — including `Content-Length`, `Host`, `Cookie`, etc. This causes `net::ERR_CONTENT_LENGTH_MISMATCH` in browsers. A companion defensive fix in better-call is at https://github.com/better-auth/better-call/pull/120. ## Root Cause In `to-auth-endpoints.ts` lines 161-172, when a before hook returns a non-context value (early return), the code was: ```typescript return context?.asResponse ? toResponse(before, { headers: context?.headers }) // ← request headers! : context?.returnHeaders ? { headers: context?.headers, response: before } // ← request headers! : before; ``` At this point in execution, `internalContext.context.responseHeaders` is `undefined` (line 117) — no response headers have been accumulated. `context?.headers` contains the **request** headers (set at line 121 from `new Headers(context?.headers)`). Passing these to `toResponse()` grafts request headers onto the response. ## Fix Remove `context?.headers` from the early-return path entirely: ```typescript return context?.asResponse ? toResponse(before) // no headers — toResponse sets its own : context?.returnHeaders ? { headers: new Headers(), response: before } // empty response headers : before; ``` This is correct because: 1. No response headers exist at this point (`responseHeaders` is `undefined`) 2. The hook's return value IS the response — it doesn't need request headers grafted onto it 3. `toResponse()` sets appropriate headers (like `Content-Type`) based on the data type ## Test plan - [x] New test: before hook early return with `asResponse: true` does not leak request headers - [x] New test: before hook early return with `returnHeaders: true` does not leak request headers - [x] Verify `content-type: application/json` is correctly set on early-return responses - [x] All 36 existing + new tests pass <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fixes a bug in `better-auth` where early returns in before hooks leaked request headers into responses, causing browser `net::ERR_CONTENT_LENGTH_MISMATCH` errors. - **Bug Fixes** - Stop passing `context.headers` to `toResponse()` on early return. - Return empty headers when `returnHeaders: true`. - Add tests to ensure no header leak and correct `content-type` is set. <sup>Written for commit 724a978259c1b8751fe0cbafc093a8e962797446. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. -->
GiteaMirror added the pull-request label 2026-04-13 10:29:36 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#16322