Skip to content

Encoding issue on binary data when using rewrites in AWS #697

@ipc-zpg

Description

@ipc-zpg

Originally reported to SST here: sst/sst#5317

When using a rewrite in NextJS in a AWS deployment, binary data appears to be being encoded incorrectly. When running locally the issue is not present.

In my case, this is causing images to be corrupted.

Tested with SST version 3.4.56, and version 3.5.0

next.config.ts

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  rewrites: async () => {
    return {
      beforeFiles: [],
      afterFiles: [],
      fallback: [
        {
          source: "/google_logo",
          destination: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_dark_color_272x92dp.png",
        },
      ]
    }
  }
};

export default nextConfig;

sst.config.ts

// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./.sst/platform/config.d.ts" />

export default $config({
  app(input) {
    return {
      name: "ipc-test",
      removal: input?.stage === "production" ? "retain" : "remove",
      protect: ["production"].includes(input?.stage),
      home: "aws",
    };
  },
  async run() {
    new sst.aws.Nextjs("MyWeb");
  },
});

Testing:

$ curl https://www.google.com/images/branding/googlelogo/2x/googlelogo_dark_color_272x92dp.png -o real.png
$ curl  http://localhost:3000/google_logo -o local.png
$ curl  https://d29t9gko77stcb.cloudfront.net/google_logo -o cloudfront.png

$ ls -la *
.rw-r--r-- ianchristian wheel  12 KB Wed Jan  8 08:36:35 2025   cloudfront.png
.rw-r--r-- ianchristian wheel 6.9 KB Wed Jan  8 08:33:40 2025   local.png
.rw-r--r-- ianchristian wheel 6.9 KB Wed Jan  8 08:31:20 2025   real.png

$ md5sum *
efa5b41728d0f52920fd230782066ee4  cloudfront.png
82381fe256523aa6a6061db820818e66  local.png
82381fe256523aa6a6061db820818e66  real.png

$ hexdump -C  real.png | head -n 3
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 02 20 00 00 00 b8  08 03 00 00 00 ed fd a7  |... ............|
00000020  29 00 00 03 00 50 4c 54  45 00 00 00 00 00 00 00  |)....PLTE.......|

$ hexdump -C  cloudfront.png | head -n 3
00000000  ef bf bd 50 4e 47 0d 0a  1a 0a 00 00 00 0d 49 48  |...PNG........IH|
00000010  44 52 00 00 02 20 00 00  00 ef bf bd 08 03 00 00  |DR... ..........|
00000020  00 ef bf bd ef bf bd ef  bf bd 29 00 00 03 00 50  |..........)....P|

Update

I have found that I can fix this partly by turning on lambda streaming:

open-next.config.ts

export default {
  default: {
    override: {
      wrapper: "aws-lambda-streaming"
    }
  }

However, this introduces 2 issues:

  • content type is application/json for everything
  • transfer speed seems awful - 8 seconds for a 1.3MB JPEG.

For content-type, I 'fixed' this by setting the header in middleware before the rewrite happens, but that's a nasty work around.

Update 2

A second work around that doesn't affect performance in the same was the first update does, is to specifically set the content type.

To prove/test this:

middleware.ts

import { NextRequest, NextResponse } from "next/server";

export async function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  if (pathname.endsWith("ct_fixed")) {
    console.log("fixing content type");
    request.headers.set("Content-Type", "image/png");
  } else {
    console.log("NOT fixing content type");
  }
  return NextResponse.next({ request });
}

next.config.ts

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  rewrites: async () => {
    return {
      beforeFiles: [],
      afterFiles: [],
      fallback: [
        {
          source: "/google_logo",
          destination: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_dark_color_272x92dp.png",
        },
        {
          source: "/google_logo_ct_fixed",
          destination: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_dark_color_272x92dp.png",
        },
      ]
    }
  }
};

export default nextConfig;

Testing:

❯ curl  https://d29t9gko77stcb.cloudfront.net/google_logo -o cloudfront-1.png
❯ curl  https://d29t9gko77stcb.cloudfront.net/google_logo_ct_fixed -o cloudfront-2.png
❯ ls -la
.rw-r--r-- ianchristian wheel  12 KB Thu Jan  9 09:56:39 2025  cloudfront-1.png
.rw-r--r-- ianchristian wheel 6.9 KB Thu Jan  9 09:56:52 2025  cloudfront-2.png

Notice here that cloudfront-1 (not content-type fixed) has a size of 12kb due to extra encoding, and cloudfront-2 (content-type fixed) has the correct size, and is not corrupted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions