Respond to Webhook: Best practice for returning multiple Set-Cookie headers (refresh token + CSRF token)?

I’m building an invoice automation application with:

N8n 2.18.5 version
MySql 8.0.46 version
React Vite 18.3.1 version (Frontend)

For authentication, my Sign In workflow generates:

Access token (returned in the response body)
Refresh token (returned as an HttpOnly cookie)
CSRF token (returned as a cookie)

The response body looks like:
{
“success”: true,
“access_token”: “…”,
“expires_in”: 1200
}
Initially, I tried adding two response headers with the same name:

Set-Cookie: refresh_token=…
Set-Cookie: csrf_token=…

However, only the second cookie reaches the frontend; the first one is omitted.

As a workaround, I’m currently returning the header value as an array:
{{
[
‘refresh_token=’ + $(‘Code:Combine Tokens’).item.json.refresh_token +
‘; HttpOnly; Secure; SameSite=Lax; Path=/refresh-token; Max-Age=1200’,
‘csrf_token=’ + $(‘Code:Combine Tokens’).item.json.csrf_token +
‘; Secure; SameSite=Lax; Path=/; Max-Age=1200’
]
}}

I’ve attached a screenshot of my Respond to Webhook node configuration for reference.

My questions are:

  • Is returning an array for the Set-Cookie response header the recommended approach in the Respond to Webhook node?

  • Does n8n internally convert this into multiple Set-Cookie headers, or is there a better way to send multiple cookies?

  • Are there any compatibility issues with browsers or proxies when using this approach?

  • Is there any planned support (or existing configuration) for sending multiple headers with the same name directly?

Hi @lekha_pm Welcome!
The Respond to Webhook node can’t emit two Set-Cookie lines. It flattens the Response Headers into a plain object keyed by header name and stringifies each value, so two set-cookie entries overwrite each other (that is why only the second one reached you), and an array value gets joined with commas into a single header. A comma-folded Set-Cookie is not valid per RFC 6265 and breaks the moment a value contains a comma (an Expires date does), so browsers and proxies won’t reliably pull both cookies out of it. The array is not a recommended approach, it only survives here because your two values happen to stay comma-free.
The clean fix is to stop trying to send two HttpOnly cookies. Only refresh_token needs to be HttpOnly, so send that as the single Set-Cookie and return csrf_token in the JSON body:

{
  "success": true,
  "access_token": "...",
  "csrf_token": "...",
  "expires_in": 1200
}

In the double-submit pattern the CSRF cookie is meant to be readable by JS anyway, so set it client-side from that body value in your React app. That leaves n8n sending one cookie, which the node handles correctly.
If both values really must be server-set HttpOnly cookies, put a reverse proxy (nginx) in front of n8n and add the second Set-Cookie there, since the node itself cannot send two.

Yes. A header like:

Set-Cookie: refresh_token=abc; HttpOnly, csrf_token=def; Secure

is not compliant with the cookie specification. Browsers treat the part after the comma as a new header value, often ignoring it. Additionally, when a cookie includes an Expires=Wed, 05 Oct 2024 14:48:00 GMT attribute (which itself contains a comma), the whole header becomes malformed and both cookies may be dropped​. Some proxies also rewrite or strip malformed Set‑Cookie headers, leading to inconsistent behaviour across environments.

At the moment n8n does not provide a built‑in way to emit duplicate header names from the Respond to Webhook node. The feature request has been discussed in the community, but the recommended pattern is to keep the response simple (one cookie) and handle any additional cookies outside n8n (e.g., via a front‑end script or a reverse‑proxy).