Preliminary Checks
Reproduction
https://github.com/BalajiSriraman/javascript/blob/fix/nuxt-preserve-set-cookie-headers/packages/nuxt/src/runtime/server/__tests__/clerkMiddleware.test.ts#L116-L143
The package's existing clerkMiddleware.test.ts can reproduce this without a Clerk instance: mock authenticateRequest() with a Headers object containing two appended Set-Cookie values, run the H3 handler, then inspect response.headers.getSetCookie(). Only the last value remains.
Publishable key
Not applicable. This is a unit-level server middleware reproduction and does not make a request to Clerk.
Description
Steps to reproduce:
- Create a
Headers instance and append two Set-Cookie values, such as an expired __clerk_handshake cookie and a refreshed __session cookie.
- Return those headers from the mocked
authenticateRequest() result used by clerkMiddleware.
- Send a request through the H3 handler and inspect
response.headers.getSetCookie().
Expected behavior:
Both cookie directives are present in the Nuxt response. Set-Cookie is a repeatable response header, and Clerk can return several cookie mutations from one authentication or handshake result.
Actual behavior:
Only the last cookie directive is present. clerkMiddleware iterates over the authentication headers and calls H3's setResponseHeader() for every value. That API replaces a previous value with the same name, so each Set-Cookie overwrites the preceding one.
This can leave an expired or stale Clerk handshake cookie in the browser when its deletion directive is followed by another cookie directive. Subsequent server requests can then continue entering the handshake path until that short-lived cookie expires.
Environment
System:
OS: Linux 6.16 Pop!_OS 24.04 LTS
CPU: (16) x64 11th Gen Intel(R) Core(TM) i9-11900K @ 3.50GHz
Binaries:
Node: 25.0.0
npm: 11.6.2
pnpm: 10.33.0
Browsers:
Chrome: 150.0.7871.46
npmPackages:
@clerk/nuxt: 3.0.15
h3: 1.15.11
Preliminary Checks
Reproduction
https://github.com/BalajiSriraman/javascript/blob/fix/nuxt-preserve-set-cookie-headers/packages/nuxt/src/runtime/server/__tests__/clerkMiddleware.test.ts#L116-L143
The package's existing
clerkMiddleware.test.tscan reproduce this without a Clerk instance: mockauthenticateRequest()with aHeadersobject containing two appendedSet-Cookievalues, run the H3 handler, then inspectresponse.headers.getSetCookie(). Only the last value remains.Publishable key
Not applicable. This is a unit-level server middleware reproduction and does not make a request to Clerk.
Description
Steps to reproduce:
Headersinstance and append twoSet-Cookievalues, such as an expired__clerk_handshakecookie and a refreshed__sessioncookie.authenticateRequest()result used byclerkMiddleware.response.headers.getSetCookie().Expected behavior:
Both cookie directives are present in the Nuxt response.
Set-Cookieis a repeatable response header, and Clerk can return several cookie mutations from one authentication or handshake result.Actual behavior:
Only the last cookie directive is present.
clerkMiddlewareiterates over the authentication headers and calls H3'ssetResponseHeader()for every value. That API replaces a previous value with the same name, so eachSet-Cookieoverwrites the preceding one.This can leave an expired or stale Clerk handshake cookie in the browser when its deletion directive is followed by another cookie directive. Subsequent server requests can then continue entering the handshake path until that short-lived cookie expires.
Environment
System: OS: Linux 6.16 Pop!_OS 24.04 LTS CPU: (16) x64 11th Gen Intel(R) Core(TM) i9-11900K @ 3.50GHz Binaries: Node: 25.0.0 npm: 11.6.2 pnpm: 10.33.0 Browsers: Chrome: 150.0.7871.46 npmPackages: @clerk/nuxt: 3.0.15 h3: 1.15.11