-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Describe the bug
When using Quarkus as an AWS Lambda handler behind API Gateway v2, multiple Set-Cookie
headers are incorrectly merged into a single header in the Lambda response. This causes browsers to fail to set multiple cookies as expected, breaking applications that rely on more than one cookie.
Expected behavior
The response should use the cookies
array field as per API Gateway v2 Lambda integration, with each cookie as a separate entry:
{
"statusCode": 200,
"headers": {
"content-length": "40",
"Content-Type": "application/json"
},
"multiValueHeaders": null,
"cookies": [
"cookie1=value1;Version=1;Path=/;Max-Age=3600;Secure;HttpOnly;SameSite=Lax",
"cookie2=value2;Version=1;Path=/;Max-Age=3600;Secure;HttpOnly;SameSite=Lax"
],
"body": "{\"status\":\"ok\",\"rootPath\":\"Hello hello\"}",
"isBase64Encoded": false
}
With this format, browsers correctly set multiple cookies.
Actual behavior
The response from PingController merges all cookies into a single Set-Cookie
header:
{
"statusCode": 200,
"headers": {
"content-length": "40",
"Set-Cookie": "cookie1=value1;Version=1;Path=/;Max-Age=3600;Secure;HttpOnly;SameSite=Lax,cookie2=value2;Version=1;Path=/;Max-Age=3600;Secure;HttpOnly;SameSite=Lax",
"Content-Type": "application/json"
},
"multiValueHeaders": null,
"cookies": null,
"body": "{\"status\":\"ok\",\"rootPath\":\"Hello hello\"}",
"isBase64Encoded": false
}
As a result, when deployed to AWS, browsers do not set multiple cookies correctly if more than one is present.
How to Reproduce?
Detailed description and steps in project https://github.com/juliusz-cwiakalski/example-bug-quarkus-aws-lambda-gateway-v2-multiple-cookies-broken
In short:
- clone the project
./gradlew build -Dquarkus.package.jar.enabled=true -Dquarkus.package.jar.type=legacy-jar -Dquarkus.native.enabled=false
sam local invoke --template ./build/sam.jvm.yaml -e ./example-gateway-event.json
Output of uname -a
or ver
Linux juliusz-Latitude-5421 6.8.0-79-generic #79-Ubuntu SMP PREEMPT_DYNAMIC Tue Aug 12 14:42:46 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Output of java -version
openjdk version "21.0.5" 2024-10-15 LTS OpenJDK Runtime Environment Corretto-21.0.5.11.1 (build 21.0.5+11-LTS) OpenJDK 64-Bit Server VM Corretto-21.0.5.11.1 (build 21.0.5+11-LTS, mixed mode, sharing)
Quarkus version or git rev
3.24.4
Build tool (ie. output of mvnw --version
or gradlew --version
)
------------------------------------------------------------ Gradle 8.14 ------------------------------------------------------------ Build time: 2025-04-25 09:29:08 UTC Revision: 34c560e3be961658a6fbcd7170ec2443a228b109 Kotlin: 2.0.21 Groovy: 3.0.24 Ant: Apache Ant(TM) version 1.10.15 compiled on August 25 2024 Launcher JVM: 21.0.5 (Amazon.com Inc. 21.0.5+11-LTS) Daemon JVM: /home/juliusz/.sdkman/candidates/java/21.0.5-amzn (no JDK specified, using current Java home) OS: Linux 6.8.0-79-generic amd64
Additional information
Root Cause Analysis
The bug is caused by the following logic in LambdaHttpHandler:
// Handle cookies separately to preserve commas in the header values
@Override
public void handleMessage(Object msg) {
try {
// ...existing code...
if (msg instanceof HttpResponse res) {
responseBuilder.setStatusCode(res.status().code());
final Map<String, String> headers = new HashMap<>();
responseBuilder.setHeaders(headers);
for (String name : res.headers().names()) {
final List<String> allForName = res.headers().getAll(name);
if (allForName == null || allForName.isEmpty()) {
continue;
}
// Handle cookies separately to preserve commas in the header values
if ("set-cookie".equals(name)) {
responseBuilder.setCookies(allForName);
continue;
}
// ...
}
}
}
}
However, in the Lambda runtime context, the msg
is of type io.vertx.core.http.impl.AssembledFullHttpResponse
, and its headers are an instance of io.vertx.core.http.impl.headers.HeadersMultiMap
, which is case-insensitive and converts all header names to Camel-Kebab-Case. Thus, the check for "set-cookie"
fails because the actual header name is "Set-Cookie"
.
Solution
Change the header name check to be case-insensitive:
if ("set-cookie".equalsIgnoreCase(name)) {
responseBuilder.setCookies(allForName);
continue;
}
Metadata
Metadata
Assignees
Labels
Type
Projects
Status