Next.js 413 Payload Too Large: Fix File Upload Errors
File uploads fail with 413 or 'Body exceeded 1MB limit' in Next.js? Where each limit lives (Server Actions, API routes, Vercel, nginx) and the upload pattern that avoids all of them.
A 413 "Payload Too Large" error in Next.js means the request body exceeded a size limit set by your framework, your host, or a proxy in front of it; the durable fix is to upload files directly from the browser to object storage with a presigned URL instead of streaming them through your server.
The limit that fires depends on where the request dies. Identify the layer first.
Which Limit Are You Hitting?
| Layer | Default limit | Error you see | Where to change it |
|---|---|---|---|
| Server Actions | 1 MB | Body exceeded 1MB limit |
serverActions.bodySizeLimit |
| Pages Router API route | 1 MB | API resolved without sending a response or 413 |
export const config = { api: { bodyParser: { sizeLimit } } } |
| Vercel serverless functions | About 4.5 MB request body | 413 FUNCTION_PAYLOAD_TOO_LARGE |
Cannot be raised; avoid the function |
| nginx | 1 MB (client_max_body_size) |
413 Request Entity Too Large |
nginx config |
| Cloudflare | Depends on plan | 413 from the edge | Plan limit or bypass |
Check your host's current numbers in its docs, since platform limits change.
Quick Fix for Small Files
For files a few megabytes in size, raise the Server Action limit:
// next.config.ts
const nextConfig = {
experimental: {
serverActions: { bodySizeLimit: '5mb' },
},
}
export default nextConfig
For a Pages Router API route:
export const config = { api: { bodyParser: { sizeLimit: '5mb' } } }
For nginx behind your app:
client_max_body_size 10m;
This works until the first user uploads a 12 MB phone photo. Then it breaks again, and on Vercel you cannot raise the platform limit at all.
The Real Fix: Presigned Direct Upload
The file goes browser to storage (S3, R2, Supabase Storage, UploadThing). Your server only signs a short-lived URL.
// app/api/upload-url/route.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const s3 = new S3Client({ region: process.env.AWS_REGION })
export async function POST(req: Request) {
const { filename, contentType, size } = await req.json()
if (size > 25 * 1024 * 1024) {
return Response.json({ error: 'File too large' }, { status: 413 })
}
const key = `uploads/${crypto.randomUUID()}-${filename}`
const url = await getSignedUrl(
s3,
new PutObjectCommand({ Bucket: process.env.S3_BUCKET!, Key: key, ContentType: contentType }),
{ expiresIn: 60 }
)
return Response.json({ url, key })
}
// client
const { url, key } = await (await fetch('/api/upload-url', {
method: 'POST',
body: JSON.stringify({ filename: file.name, contentType: file.type, size: file.size }),
})).json()
await fetch(url, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } })
Validate size and type on the server before signing, and set a bucket CORS rule that allows PUT from your origin. A missing CORS rule produces a different failure; see CORS errors in production.
Return a Clear Error, Not a Generic 500
Reject oversized files in the browser first (file.size), then again on the server with a 413 and a readable message. Handle the response in your route as shown in Next.js API routes error handling so users never see a blank failure.
Why This Bug Goes Unnoticed
Large uploads fail for a small share of users, usually on mobile with high-resolution photos. The failure happens at the platform layer, before your code runs, so there is often no application log at all. Your support inbox finds it first.
Let Relia Find the Silent Failures
Relia captures the user's session behind the failed request and ties it to the error, so a spike of failed uploads shows up with the route involved and the likely cause. On request it writes a fix plan with a "Copy for your AI assistant" button; Relia doesn't change code itself, and after you ship it checks the fix against live traffic.
FAQ
What is the Vercel upload size limit?
Request bodies to serverless functions are capped at roughly 4.5 MB. For larger files, upload directly to storage with a presigned URL.
How do I increase the body size limit in Next.js?
Set serverActions.bodySizeLimit for Server Actions, or bodyParser.sizeLimit for Pages Router API routes. Neither raises your host's own limit.
Why do I get 413 only in production?
Production has a proxy or platform limit that localhost does not. Check nginx, your CDN, and the host's function payload cap.