DevLog;

Next.js 16 File Uploads Broken by Proxy Handle

Cover Image for Next.js 16 File Uploads Broken by Proxy Handle
Milan Medvec
Milan Medvec

Last week, we upgraded our project from Next.js v15.15 to the new version v16.0. After the upgrade, we started experiencing issues with file uploads. I debugged the problem using an empty project to isolate the cause and discovered that there is a bug in the new proxy handler.

How to reproduce it?

Create Next.js app:

npx create-next-app@latest nextjs-form-reset

And create / update these 4 files:

  • app/state.ts
export interface State {}
  • app/action.ts
"use server";

import { State } from "./state";

export async function action(state: State, formData: FormData): Promise<State> {
    console.log("Action called with: ", formData);

    return state;
}
  • app/page.tsx
"use client";

import { useActionState } from "react";
import { State } from "./state";
import { action } from "./action";

export default function () {
    const [state, formAction, isPending] = useActionState<State, FormData>(action, {});

    return (
        <form action={formAction} method="POST" encType="multipart/form-data">
            <div>
                <input type="file" name="file" />
            </div>
            <div>
                <button type="submit">Submit</button>
            </div>
        </form>
    );
}
  • next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
  experimental: {
    proxyClientMaxBodySize: '20mb',
    serverActions: {
      bodySizeLimit: '20mb',
    },
  },
};

export default nextConfig;

When you run the app and submit the form with a larger file (I’m using a 10MB image), you will see the following correct output in the next console:

Action called with:  FormData {
  '$ACTION_REF_1': '',
  '$ACTION_1:0': '{"id":"60d9aa708d193f64ed39d6103bacccd511e5f972c2","bound":"$@1"}',
  '$ACTION_1:1': '[{}]',
  '$ACTION_KEY': 'kfc8fdef6421490deff683859af2a2eff',
  file: File {
    size: 12583050,
    type: 'image/bmp',
    name: 'empty_10mb.bmp',
    lastModified: 1763298853354
  }
}

Now when you create proxy.ts file in root folder, and add the following code:

export default async function proxy(req: Request) {}

and run the app again, you’ll see the following in the next console:

Action called with:  FormData {}
  • It seems the form data is somehow missing.

I’m not sure why this is happening, but I am sure it is not related to upload limit, or Next.js configuration. I’m guessing it’s related to the new proxy handler - for me it seems like a race condition.

Workaround

Since we cannot remove the proxy handler, we need a workaround. I found a solution that works for me, although it’s not ideal: awaiting the proxy execution until the POST body is fully loaded.

I added this ugly snippet to proxy handler:

try {
  if (req.method === "POST") {
    await req.formData();
  }
} catch (error) {
  ...
}

I am going to try to find a better solution, but for now this is the best workaround I found.