Fixing Cookie Loss When Proxying from Fastify → Next.js
Intro
Ran into a weird issue while proxying requests from Fastify into Next.js:
Next.js won't preserve cookies when rendering pages via a custom server. Even if your backend (Fastify) handles cookies correctly, req.cookies
and Set-Cookie
headers disappear downstream.
What I ended up doing: store cookies temporarily on a custom property (called tempCookies
) on the Fastify request, and then inject them back into the headers before calling Next.js' request handler. Feels scratch-your-head at first, but it works ⚙️
The Problem
When forwarding a request into Next.js using something like:
1await handle(req.raw, reply.raw)
Next.js ignores Fastify's req.cookies
and even strips any Set-Cookie
headers sent before it. Essentially:
- Browser → Fastify Request
- Fastify sets & reads cookies ok
Set-Cookie
header ends up lost- In
handle(req.raw,…)
Next.js overrides headers
The Workaround (My Solution)
1. Extend FastifyRequest
Add a custom property to save cookies across the proxy boundary:
1declare module 'fastify' {2 interface FastifyRequest {3 tempCookies?: string[]4 }5}
2. In nextjsCustomServer.ts
Before forwarding to Next.js, re-attach the saved cookies:
1serve.all('/*', async (req, reply) => {2 if (Array.isArray(req.tempCookies) && req.tempCookies.length > 0) {3 reply.raw.setHeader('Set-Cookie', req.tempCookies)4 }5 await handle(req.raw, reply.raw)6 reply.hijack()7})
That ensures Next.js' HTTP response includes any cookie headers Fastify accumulated.
3. setCookie()
helper logic (backend response → client propagation)
When Fastify receives a Set-Cookie
header from another service or API, capture and replay it:
1export function setCookie(rawSetCookie: string, req: FastifyRequest) {2 // Simplified for this note: parse key=value from raw header3 const [whatToSet] = rawSetCookie.split(';')4 req.tempCookies = (req.tempCookies ?? []).concat(rawSetCookie)56 const [name, value] = whatToSet.split('=')7 if (value === '' && req.cookies) {8 delete req.cookies[name]9 } else {10 req.cookies = req.cookies || {}11 req.cookies[name] = value12 }1314 // Sync headers so Next.js can read them in SSR15 const parsed = parse(req.headers.cookie ?? '')16 value === '' ? delete parsed[name] : (parsed[name] = value)17 req.headers.cookie = Object.entries(parsed)18 .map(([k, v]) => `${k}=${v}`)19 .join('; ')20}
Now setCookie()
does three things:
- Stores the original
Set-Cookie
header inreq.tempCookies
. - Updates
req.cookies
object so Fastify continues logic near-term. - Rewrites
req.headers.cookie
so Next.js' internal cookie parser sees the new or deleted cookie.
🤔 Why it happens & what docs say
- Next.js re-constructs cookies for SSR based on the raw Node HTTP headers, not using Fastify's
req.cookies
. - You have to manually sync cookies if you use a custom server proxy.
- Articles on header forwarding (e.g. Jon Meyers' blog) confirm: Next.js Server Components paired with Route Handlers won't forward cookies automatically—you must forward them in
fetch()
calls or wrapper layers
🧪 Caveats & gotchas
- If
tempCookies
is large or you add duplicates repeatedly, it can bloat headers. You can dedupe by cookie name if needed. - If setting multiple cookies with different attributes, always append them to an array when calling
setHeader('Set-Cookie', …)
. - Be cautious with domains,
httpOnly
,SameSite
, etc—in local development your domain matching logic might differ.
✅ Summary
- Next.js doesn't automatically propagate cookies when you use a custom server proxy.
- Fastify's
req.cookies
andres.setHeader()
must be translated into raw HTTP headers manually. - My workaround: store cookies in
req.tempCookies
, reattach before calling Next.js, and rewrite thecookie
header. - This lets my Next.js SSR see the correct cookies for auth/session logic—and the browser gets the actual
Set-Cookie
instructions.
Now I can move cookies around the proxy boundary without them vanishing in thin air 😅