How do I bypass the 4.5MB body size limit of Vercel Serverless Functions?

When trying to request or upload a large file from a Serverless Function, you may have seen a 413: FUNCTION_PAYLOAD_TOO_LARGE error. This is because Serverless Functions have a payload limit of 4.5mb for request and response bodies. Read on to learn more about how you can work with this limitation.

Request Directly From The Source

Serverless Functions are designed to respond quickly to clients and should be treated like a lightweight API layer, not a media server.

If you have a large file like a video that you need to send to a client, you should consider storing those assets in a dedicated media host and make them retrievable with a presigned URL that contains access control policies directly in the URL.

This will ensure that your assets are securely accessed in a manner that you control. CloudinaryFaunaDB, and AWS S3 are all examples of services that support this.

Upload Directly To The Source

Similary to requesting assets securely, you can upload large files directly to a media host from your browser without needing a Serverless Function as a proxy. Vercel Blob, CloudinaryFaunaDB, and AWS S3 are all solutions for this among others.

If you need to upload files larger than 4.5 MB to Vercel Blob, you can use client uploads, where the file is sent directly from the client (e.g. a browser) to Vercel Blob. This transfer is done securely as to not expose your Vercel Blob store to anonymous uploads. The security mechanism is based on a token exchange between your server and Vercel Blob.

Step 1: Create a client upload page

This page allows to upload files to Vercel Blob. The files will go directly from the browser to Vercel Blob without going through your server.Behind the scenes, the upload is done securely by exchanging a token with your server before uploading the file.

app/avatar/upload/page.tsx
'use client';
import { type PutBlobResult } from '@vercel/blob';
import { upload } from '@vercel/blob/client';
import { useState, useRef } from 'react';
export default function AvatarUploadPage() {
const inputFileRef = useRef<HTMLInputElement>(null);
const [blob, setBlob] = useState<PutBlobResult | null>(null);
return (
<>
<h1>Upload Your Avatar</h1>
<form
onSubmit={async (event) => {
event.preventDefault();
const file = inputFileRef.current.files[0];
const newBlob = await upload(file.name, file, {
access: 'public',
handleUploadUrl: '/api/avatar/upload',
});
setBlob(newBlob);
}}
>
<input name="file" ref={inputFileRef} type="file" required />
<button type="submit">Upload</button>
</form>
{blob && (
<div>
Blob url: <a href={blob.url}>{blob.url}</a>
</div>
)}
</>
);
}

Step 2: Create a client upload route

The responsibility of this client upload route is to:

  1. Generate tokens for client uploads
  2. Listen for completed client uploads, so you can update your database with the URL of the uploaded file for example

The @vercel/blob npm package exposes a helper to implement said responsibilities.

app/api/avatar/upload/route.ts
import { handleUpload, type HandleUploadBody } from '@vercel/blob/client';
import { NextResponse } from 'next/server';
export async function POST(request: Request): Promise<NextResponse> {
const body = (await request.json()) as HandleUploadBody;
try {
const jsonResponse = await handleUpload({
body,
request,
onBeforeGenerateToken: async (
pathname: string,
/* clientPayload?: string, */
) => {
// Generate a client token for the browser to upload the file
// ⚠️ Authenticate users before generating the token.
// Otherwise, you're allowing anonymous uploads.
const { user } = await auth(request);
const userCanUpload = canUpload(user, pathname);
if (!userCanUpload) {
throw new Error('Not authorized');
}
return {
allowedContentTypes: ['image/jpeg', 'image/png', 'image/gif'],
tokenPayload: JSON.stringify({
// optional, sent to your server on upload completion
userId: user.id,
}),
};
},
onUploadCompleted: async ({ blob, tokenPayload }) => {
// Get notified of client upload completion
// ⚠️ This will not work on `localhost` websites,
// Use ngrok or similar to get the full upload flow
console.log('blob upload completed', blob, tokenPayload);
try {
// Run any logic after the file upload completed
// const { userId } = JSON.parse(tokenPayload);
// await db.update({ avatar: blob.url, userId });
} catch (error) {
throw new Error('Could not update user');
}
},
});
return NextResponse.json(jsonResponse);
} catch (error) {
return NextResponse.json(
{ error: (error as Error).message },
{ status: 400 }, // The webhook will retry 5 times waiting for a 200
);
}
}

When your local website is served on http://localhost:3000, then the onUploadCompleted step won't succeed as Vercel Blob cannot contact your localhost. Instead, we recommend you run your local application through a tunneling service like ngrok , so you can experience the full Vercel Blob development flow locally.

Couldn't find the guide you need?