You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Function URL with Response Streaming does not support Content-Encoding #96
If I compress my response using gzip and set a proper Content-Encoding response header, it appears like Function URL will decompress my response on-the-fly, remove the Content-Encoding header, and pass down the uncompressed response stream. This is wasteful and unnecessary.
We're experiencing a related but more severe manifestation of this issue when CloudFront is in front of the Lambda Function URL.
Our setup
Lambda Function URL with RESPONSE_STREAM invocation mode
The application-level wrapper (OpenNext's aws-lambda-streaming) compresses responses using Node.js zlib (brotli/gzip) based on Accept-Encoding, sets Content-Encoding in the application/vnd.awslambda.http-integration-response prelude, and pipes compressed chunks into the Lambda response stream
CloudFront sits in front of the Function URL as the CDN
What we observe
Unlike the original report (where the header is stripped and the client gets valid uncompressed data), we get periodic 502 errors from CloudFront. The Lambda function logs show successful execution — the failure happens between the Function URL and CloudFront.
On the client side:
/api/auth/session:1 Failed to load resource: the server responded with a status of 502 ()
Unexpected token 'I', "Internal S"... is not valid JSON
Why our symptom differs from the original report
The original report tests with a direct client → Function URL setup and observes: body decompressed, header stripped, client receives valid uncompressed data.
Our setup has CloudFront → Function URL, and we get 502s. This suggests the Function URL's interference with Content-Encoding manifests differently depending on the consumer. The most likely explanation:
Header retained, body decompressed: The Function URL decompresses the body but does NOT strip the Content-Encoding header when CloudFront is the downstream consumer. CloudFront sees Content-Encoding: br with an uncompressed body → mismatch → 502.
We have not been able to capture the raw HTTP response between the Function URL and CloudFront to confirm the exact mechanism, but the core problem is the same: the Function URL infrastructure should not interfere with Content-Encoding at all.
Our workaround
Deleting accept-encoding / Accept-Encoding from the request in the CloudFront viewer request function before it reaches the Lambda origin:
Without Accept-Encoding, the streaming wrapper falls back to identity (no compression, no Content-Encoding header set). The Function URL has nothing to interfere with. CloudFront handles compression to the end client. 502 errors resolved completely.
Impact
This affects anyone using the common architecture of CloudFront → Lambda Function URL (streaming mode) → application-level compression — which includes all OpenNext/SST deployments using the aws-lambda-streaming wrapper (the default for Next.js on AWS Lambda).
The workaround works but is suboptimal: it forces uncompressed transfer between Lambda and CloudFront, increasing data transfer costs and latency for large responses. The correct fix would be for the Function URL infrastructure to pass through Content-Encoding responses without interference.
Activity
Helen-Zhao commentedon Mar 25, 2026
We're experiencing a related but more severe manifestation of this issue when CloudFront is in front of the Lambda Function URL.
Our setup
RESPONSE_STREAMinvocation modeaws-lambda-streaming) compresses responses using Node.js zlib (brotli/gzip) based onAccept-Encoding, setsContent-Encodingin theapplication/vnd.awslambda.http-integration-responseprelude, and pipes compressed chunks into the Lambda response streamWhat we observe
Unlike the original report (where the header is stripped and the client gets valid uncompressed data), we get periodic 502 errors from CloudFront. The Lambda function logs show successful execution — the failure happens between the Function URL and CloudFront.
On the client side:
Why our symptom differs from the original report
The original report tests with a direct client → Function URL setup and observes: body decompressed, header stripped, client receives valid uncompressed data.
Our setup has CloudFront → Function URL, and we get 502s. This suggests the Function URL's interference with
Content-Encodingmanifests differently depending on the consumer. The most likely explanation:Content-Encodingheader when CloudFront is the downstream consumer. CloudFront seesContent-Encoding: brwith an uncompressed body → mismatch → 502.We have not been able to capture the raw HTTP response between the Function URL and CloudFront to confirm the exact mechanism, but the core problem is the same: the Function URL infrastructure should not interfere with
Content-Encodingat all.Our workaround
Deleting
accept-encoding/Accept-Encodingfrom the request in the CloudFront viewer request function before it reaches the Lambda origin:Without
Accept-Encoding, the streaming wrapper falls back toidentity(no compression, noContent-Encodingheader set). The Function URL has nothing to interfere with. CloudFront handles compression to the end client. 502 errors resolved completely.Impact
This affects anyone using the common architecture of CloudFront → Lambda Function URL (streaming mode) → application-level compression — which includes all OpenNext/SST deployments using the
aws-lambda-streamingwrapper (the default for Next.js on AWS Lambda).We've also filed this on the OpenNext side: opennextjs/opennextjs-aws#1127
The workaround works but is suboptimal: it forces uncompressed transfer between Lambda and CloudFront, increasing data transfer costs and latency for large responses. The correct fix would be for the Function URL infrastructure to pass through
Content-Encodingresponses without interference.Environment
RESPONSE_STREAMinvocation modeapplication/vnd.awslambda.http-integration-response(prelude + 8 null bytes + body)BROTLI_OPERATION_FLUSHper chunk) / gzip (Z_SYNC_FLUSH)